ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Slang IR 类型系统全解析:`Type` 指令族、类型折叠机制与 `-dump-ir` 排错实战

Slang IR 类型系统全解析:`Type` 指令族、类型折叠机制与 `-dump-ir` 排错实战 Slang IR 类型系统全解析Type指令族、类型折叠机制与-dump-ir排错实战【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang导读本文面向需要阅读 Slang 中间表示IRdump、编写 IR pass 或后端代码的编译器工程师系统梳理 Slang IR 中规模最大的Type指令族它包含 170 个具体 opcode涵盖标量、数组、函数、资源、纹理、微分、existential、指针、速率与集合论类型等全部类别。文章将说明类型指令一等公民的设计IRType本身就是一个IRInst、绝大多数类型可提升hoistable从而使类型相等退化为指针比较的机制以及类型在-dump-ir中的折叠打印规则并结合仓库源码、门控测试与slangc -dump-ir实操示例帮你快速识别任意类型 opcode并修正关于上下文通道、Enum、Result等类型的常见误解。一、Type族在指令集中的地位170 个 opcode 的巨无霸Slang IR 把类型做成了一等 IR 值每一个IRType都只是一个结果类型为某个Kindopcode 的IRInst。整个Type族在 slang-ir-insts.lua 中是一个从第 19-20 行开始、到第 881 行结束的顶层条目在文档所锚定的source_commit上持有170 个具体 opcode占全部 857 个指令的四分之一还多——是整个指令集中最大的单一指令族见 types.md.gap-intake.md 对应的目标文档 types.md。绝大多数叶子类型 opcode 是**可提升hoistable**的同一类型在模块内去重为唯一一个 IR 值这让类型相等判断退化为一次指针比较。例外的是三类父/容器类型Enum、struct、class标记为parent全局类型interface标记为global三个无 flag 的辅助条目AfterBaseType、MakeTensorAddressingTensorLayout、MakeTensorAddressingTensorView。每个 opcode 的 flag 以参考文档逐行表格中的 Flags 列为准。二、三条命名规则Lua key、kIROp_枚举与 C wrapper阅读 IR dump 时最容易踩坑的是命名对应关系参考文档明确了三条规则枚举名 kIROp_ 条目的struct_name而不是 Lua key。instEnums生成器在 slang-ir.h.lua第 276 行发射kIROp_$(value.struct_name)。于是 Lua keyVec对应kIROp_VectorTypeArray对应kIROp_ArrayType而 Lua keyTextureShapeCubeDType对应的枚举是kIROp_TextureShapeCubeType注意没有D。Lua key 则作为-dump-ir打印的**助记符mnemonic**存活。C wrapper IR 同名struct_name。当struct_name缺省时processslang-ir-insts.lua 第 3452 行会用to_pascal_case从 key 推导。wrapper 分布在两个头文件抽象中间类与历史手写叶子如IRFuncType、IRStructType、IRClassType、IRThisType、IRInterfaceType、IRConjunctionType、IRWitnessTableType、IRBoundInterfaceType在 slang-ir.h而IRSetTagType、IRTaggedUnionType、IRElementOfSetType、IRUntaggedUnionType等slang-ir-insts.h 第 3050-3081 行与其余 154 个由 FIDDLE 模板同文件第 3113-3154 行生成的叶子在 slang-ir-insts.h。Builder 方法同样由模板生成IRBuilder::getStructName(...)如getVectorType、getArrayType、getMetalPackedVectorType由 slang-ir.h.lua第 327 行的getBasicTypesForBuilderMethods驱动。有九个叶子被排除第 333 行需要手写逻辑BindExistentialsType、BoundInterfaceType、BackwardDiffIntermediateContextType、AttributedType、RefParamType、BorrowInParamType、FuncType以及具有同一性语义、使用createStructType/createClassType而非去重get*的StructType和ClassType。三、AST 类型到 IR 类型的下降visitDeclRefType与__intrinsic_type类型下降集中在 slang-lower-to-ir.cpp 的类型访问器中分为两派手写个案visitBasicExpressionType第 2852 行、visitVectorExpressionType第 2857 行、visitMatrixExpressionType第 2865 行、visitArrayExpressionType第 2874 行、visitPtrType第 2773 行、visitTupleType第 2830 行、visitValuePackType第 2824 行、visitDifferentialPairType第 2695 行通用机制visitDeclRefType第 2809 行检测IntrinsicTypeModifier——即 core module 源码里的__intrinsic_type(...)属性——并把类型路由到lowerSimpleIntrinsicType第 2892 行后者直接取出 modifier 中的 opcode并把DeclRef的每个泛型实参按声明顺序变成 IR 类型的操作数。visitResourceType、visitSamplerStateType、visitBuiltinGenericType、visitUntypedBufferResourceType、visitHLSLPatchType、visitMeshOutputType第 2917-2921 行都只是委托给它。承载__intrinsic_type(...)属性的 core module 文件属于参考文档的watched_pathssource/slang/core.meta.slang、source/slang/hlsl.meta.slang以及声明了十个WorkGraphRecordTypeBaseopcode 的 workgraph.slang。唯一确认缺失的是source/slang/slang-ir.h.lua——它同时掌握kIROp_*命名规则第 276 行和 Flags 列背后的flagMap第 228-234 行gap-intake 报告建议将其加入 manifest。四、-dump-ir中的类型打印规则折叠与名义类型参考文档给出了一条支配所有 opcode 打印形式的规则shouldFoldInstIntoUsesslang-ir.cpp 第 7822-7859 行把所有类型折叠进使用点dumpInstExpr第 8337 行把折叠后的指令打印为助记符(操作数, ...)——因此类型在使用处读作Enum(Int)、StructuredBuffer(Float, Std140Layout, ...)零元类型读作裸助记符Float、UntypedResourceHandle。四个名义nominal类型 opcode——struct、class、GLSLShaderStorageBuffer、interface——被排除在折叠之外各自拥有顶层定义行使用点通过%id引用。其中只有前三个由dumpIRParentInst第 8358 行的特判 switch连同大括号内的子节点一起打印interface走普通路径打印为let %I : Type interface(...)其需求条目requirement entries位于操作数列表中例如interface_req_entry(%key, Func(Float, this_type(%IShape)))这一形式由门控测试interface-and-this-type.slang固定。五、上下文通道类型三族属性决定BwdCallable与MinimalContext并非二选一逆模式自动微分通过**上下文通道context channel**在调用图间传递记录下的 primal 侧状态每个函数的通道类型按函数值键控。参考文档最重要的修正结论是函数获得哪一族上下文通道是在语义检查期由 primal 函数上的微分属性决定的而非特化 pass 按状态大小选择。检查器IBackwardDifferentiable扩展合成的两个 struct——BwdCallable与MinimalContext——的irOp被各属性分支直接设置slang-check-decl.cpp 第 15153-15183、15227-15268、19210-19277 行primal 函数上的属性BwdCallableopcodeMinimalContextopcode[Differentiable]、[BackwardDifferentiable]BackwardDiffIntermediateContextTypeBackwardDiffMinimalContextType[TreatAsDifferentiable]TrivialBackwardDiffIntermediateContextTypeTrivialBackwardDiffMinimalContextType[BackwardDerivative(fn)]、[BackwardDerivativeOf(fn)]BackwardContextFromLegacyBwdDiffFuncBackwardMinimalContextFromLegacyBwdDiffFunc两个列不是互斥选择对每个可微函数两个 struct 都会被合成并同时出现在该函数的IBackwardDifferentiablewitness table 中。对[Differentiable] float f(float x) { return x * x; }并在 shader 中调用bwd_diff(f)-dump-ir会打印witness_table_entry(%30,BackwardDiffIntermediateContextType(%f)) witness_table_entry(%29,BackwardDiffMinimalContextType(%f))二者在同一个witness_table中。把f改标[TreatAsDifferentiable]两者被替换为Trivial*对把导数移入用户手写的[BackwardDerivative(f_bwd)]则替换为FromLegacy*对其中用户函数作为第二个操作数携带BackwardContextFromLegacyBwdDiffFunc(%f, %f_bwd_diff)。这一对按角色分工而非按状态大小分工apply_bwd在 primal 结果旁返回 minimal contextFunc(tuple_type(Float, BackwardDiffMinimalContextType(%f)), Float)remat把它展开为完整上下文Func(BackwardDiffIntermediateContextType(%f), BackwardDiffMinimalContextType(%f), Float)propagate 函数则消费完整上下文。属性拼写可核对 core.meta.slang[Differentiable(order:int 0)]第 470 行、[BackwardDifferentiable]第 451 行、[TreatAsDifferentiable]第 131 行与 diff.meta.slang[BackwardDerivative]第 83 行、[BackwardDerivativeOf]第 154 行。g ap-intake 报告还指出检查器仅在 slang-check-decl.cpp 第 3727-3728 行说明MinimalContext 将持有与 BwdCallable 相同的数据后续 pass 才决定其确切内容——特化 pass 选择上下文的说法是文档缺陷而非编译器行为。六、常见类型的落地细节6.1 标量基础类型与intptr_t/size_t别名BasicType的每个子类型都是可提升的每个标量类型在模块内只有一个 IR 值。IRBuilder::getBasicType(BaseType)负责从 ASTBaseType映射到正确 opcode。值得注意的修正intptr_t与uintptr_t是着色器可达的非仅 host 侧extension intptr_t/extension uintptr_t位于 core.meta.slang 第 1682 / 1692 行//public:标记自第 1642 行起而size_t/usize_t/ssize_t是第 20-24 行的 typedefsize_t与usize_t是uintptr_t的别名ssize_t是intptr_t的别名。基础类型表见 slang-embedded-core-module-source.cpp 第 107、150 行。6.2 仅存储浮点类型FloatE4M3/FloatE5M2/BFloat16三者是 core module 的普通公开structcore.meta.slang 第 1705-1751 行位于//public:下因此在 shader 源码中可直接使用例如struct S { BFloat16 b; FloatE4M3 e; FloatE5M2 f; }三个字段是三个不同 opcode而不是Half的别名。但它们不参与算术每个只符合IFloatingPointCoopElement不符合__BuiltinArithmeticType值必须先转换为内建浮点类型float(s.b)经由第 1754 行起的extensionT : __BuiltinFloatingPointType构造器。每个还带有[require(...)]能力门8 位形式为spvFloat8EXT/cuda_sm_8_9BFloat16为spvBFloat16KHR/cuda_sm_8_0。struct 字段用法由门控测试storage-only-float-struct-fields.slang固定。6.3Enum(tagType)cases 既不是操作数也不是子节点Enum是带Pflag 的 parent opcode但其唯一操作数是tagType存储枚举值的基础整数类型。IRBuilder::createEnumType在EnumDecl下降时以该 tag type 构建slang-lower-to-ir.cpp 第 12673 行createEnumType在 slang-ir.cpp 第 5346 行只传tagType。visitEnumCaseDecl第 12631 行把每个 case 下降为其 tag 表达式的值——一个类型为Enum的普通常量而非Enum的成员。P只表示该 opcode可以持有子节点。由于Enum不是名义类型它被折叠进使用点enum Color { Red 3, Green 7 }只在被引用处显示为Enum(Int)。门控测试 enum-tag-type.slang 用filecheck固定了这一打印形式。6.4Result真正的生产者是throws子句 错误处理 passResult在 AST 侧没有对应类型类source/slang/slang-ast-type.h中无匹配core module 中也没有Result拼写。throws E子句先下降为Func(T, params..., FuncThrowTypeAttr(E))slang-lower-to-ir.cpp 第 4824 行随后错误处理 pass slang-ir-lower-error-handling.cpp第 43、93 行把它重写为Func(Result(T, E), params...)。因此int f(int) throws MyErr最终呈现为Func(Result(Int, Enum(...)), Int)——门控测试 result-type-from-throws.slang 正是用// IR: func %risky{{.*}}: Func(Result(Int, Enum(固定这一点。6.5 缓冲布局标记L是StructuredBuffer的第二个泛型参数六个布局标记是满足IBufferDataLayout的真实 core module 类型hlsl.meta.slang 第 23-71 行经lowerSimpleIntrinsicType进入 IR另外四个D3DConstantBufferLayout、MetalParameterBlockLayout、CUDALayout、LLVMLayout由 slang-ir-layout.cpp 第 1051-1061 行的目标布局逻辑选择无源码拼写。命名标记落在缓冲区类型的数据布局操作数上L是StructuredBufferT, L : IBufferDataLayout DefaultDataLayout同文件第 5970 行的第二个泛型参数所以StructuredBufferfloat, Std140DataLayout b;的类型是StructuredBuffer(Float, Std140Layout, ...)而未注解的RWStructuredBufferfloat得到RWStructuredBuffer(Float, DefaultLayout, ...)。六个标记中有两个带能力门Std140DataLayout/Std430DataLayout分别[require(spirv)]/[require(glsl)]。打印形式由门控测试buffer-layout-marker-operands.slang固定。6.6Ptr的可选操作数与SPIRVUntypedPtrPtr(valueType, accessQualifier?, addressSpace?, dataLayout?)在 pointee 类型之外最多携带三个可选IRIntLit访问限定只读/只写/读写、地址空间global、shared、constant…、数据布局标记。由于操作数可选且按位置排列IRPtrTypeBaseslang-ir.h 第 1583 行提供默认值AccessQualifier::ReadWrite与AddressSpace::Generic因此判断地址空间是否真实记录应调用hasAddressSpace()。四个参数模式变体并非由独立的 AST 类型单独选择lowerFuncType式参数下降在 slang-lower-to-ir.cpp 第 4740 行按paramInfo.actualParamPassingModeToUse切换并包裹类型。SPIRVUntypedPtr操作数布局与Ptr完全相同区别只在发射SPIR-V 后端写为OpTypeUntypedPointerKHR字段/元素地址经OpUntypedAccessChainKHR下降slang-emit-spirv.cpp。它从不由下降产生processConstantBufferDescriptorHeapLoadslang-ir-spirv-legalize.cpp第 1307-1337 行只重写由ConstantBuffer类型化的SPIRVLoadDescriptorFromHeap而唯一发射它的地方是defaultGetDescriptorFromHandle中__target_switch的case spvDescriptorHeapEXT分支hlsl.meta.slang 第 27708-27807 行——该能力定义为SPV_EXT_descriptor_heap SPV_KHR_untyped_pointers。因此普通-target spirv编译不产生任何非类型化指针ConstantBufferT cb ResourceDescriptorHeap[i];走默认的__castDescriptorHandleToResource分支。6.7 未类型化句柄UntypedResourceHandle/UntypedSamplerHandle这两个零元类型是ResourceDescriptorHeap[i]/SamplerDescriptorHeap[j]产物的 IR 身份刻意不透明、不携带操作数——uint堆索引活在值里而非类型里由四个转换 opcodeCastUIntToUntypedResourceHandle、CastUntypedResourceHandleToUInt、CastUIntToUntypedSamplerHandle、CastUntypedSamplerHandleToUInt移入移出。两者都不到达后端lowerUntypedResourceHandleToUIntslang-ir-lower-dynamic-resource-heap.cpp从 slang-emit.cpp 第 1950 行运行把每个 cast 前推到uint操作数并删除之布局与发射把幸存者视为内部错误。它们可见的位置是 lower 阶段的-dump-ir快照堆下标被声明为返回它们hlsl.meta.slang 第 27590-27607 行于是ResourceDescriptorHeap[i]产生一个类型为UntypedResourceHandle的let具体资源类型只出现在转换结果上。门控测试untyped-resource-handle.slang与untyped-sampler-handle.slang固定了这一 dump 形态。6.8 工作图记录类型无 AST 侧对应类的十种 opcode十个WorkGraphRecordTypeBaseopcode 在本族中属于异类它们在 AST 侧完全没有对应类。每个都在 workgraph.slang 中以仅__intrinsic_type(...)声明例如NodeOutputArray第 223-226 行、EmptyNodeOutputArray第 261-264 行检查器只看到 core-modulestruct上的普通DeclRefTypeopcode 在lowerSimpleIntrinsicType读取IntrinsicTypeModifier并把唯一泛型实参T变成操作数 0 时才首次物化。三个Empty*变体是零元的因为其记录无负载。要实际产生其中之一需要四件事同时满足见 workgraph.slang 第 9-11 行的[ExperimentalModule] module workgraph;声明import experimental.workgraph;-experimental-feature选项——模块被声明为[ExperimentalModule]slang-lower-to-ir.cpp 第 15449-15451 行将其转为ExperimentalModuleDecoration模块加载器在未设置CompilerOptionName::ExperimentalFeature时诊断NeedToEnableExperimentFeatureslang-session.cpp 第 1766-1774 行以-stage node编译[shader(node)]入口-profile lib_6_8或更新的 profile——因为node阶段能力定义为_node _sm_6_8slang-capabilities.capdef 第 1532 行。workgraph-*.slang六个测试完整覆盖了这些 flag。6.9Vec/Mat与矩阵布局Vec(elementType, elementCount)是 HLSLvectorT,N的 IR 编码。Mat(elementType, rowCount, columnCount, layout)增加两个形状操作数layout是持有SlangMatrixLayoutMode值的 int 字面量——0未知、1行主序、2列主序这三个值同样被 core module 拼接进kRowMajorMatrixLayout/kColumnMajorMatrixLayoutcore.meta.slang 第 2295-2296 行。matrixT, R, C, L的第四个泛型参数默认unknown第 2301 行visitMatrixExpressionType第 2865 行把 AST 持有的值直接放进操作数 3普通float4x4以0下降row_major/column_major修饰符在检查期钉住1/2而specializeMatrixLayoutslang-ir-specialize-matrix-layout.cpp第 28-46 行由 slang-emit.cpp 第 1366 行运行在发射前把幸存的0重写为目标-matrix-layout-row-major/-matrix-layout-column-major设置未设置时默认行主序。三个SlangMatrixLayoutMode值本身定义在 slang.h 第 899-901 行。6.10Conjunction类型约束在泛型签名中不可见Conjunction的 AST 来源是AndType——类型运算符例如泛型约束T : IA IBvisitAndTypeslang-lower-to-ir.cpp 第 3004 行。但emitGenericConstraintValue第 13075 行把用作泛型约束的合取超类型分解为每个 case 一个witness_table参数并返回MakeTuple因此T : IA IB约束在下降后留下相互独立的 witness-table 约束泛型签名里没有Conjunction——这正是该 opcode 在 dump 中极少可见的原因。6.11MetalPackedVec、AnyValueType、RateQualified与TaggedUnionTypeMetalPackedVec形状与Vec相同但携带 Metal 自然的标量对齐紧密打包布局32 位 float 的 3 向量占 12 字节、无尾部填充。无 AST 来源由 Metal 缓冲区元素类型合法化slang-ir-lower-buffer-element-type.cpp经生成的IRBuilder::getMetalPackedVectorType合成Metal 发射器在 slang-emit-metal.cpp 中打印为 MSLpacked_TN如packed_float3。AnyValueType(size)固定大小、类型擦除的值 blobsize记录 blob 必须容纳的最大字节数由 existential 消除 pass 按可流经该 existential 的具体类型并集选择。RateQualifiedRateQualifiedType(rate, valueType)编码编译期/运行期区分——RateQualified(ConstExprRate, int)是编译期已知的intRateQualified(SpecConstRate, int)是特化期已知。速率作为类型操作数而非独立限定符使去重机制把速率相同的类型视为同一 IR 值。设计理由见 design/ir.md。TaggedUnionType操作数顺序陷阱Lua 注释写作TaggedUnionType(typeSet, witnessTableSet)但实际顺序相反——IRBuilder::getTaggedUnionType(IRWitnessTableSet* tables, IRTypeSet* types)slang-ir-insts.h 第 4579 行把tables放操作数 0、types放操作数 1访问器getWitnessTableSet()/getTypeSet()第 3067 行按此读取。务必使用访问器按注释做位置读取会把两个集合交换。七、TextureType的九操作数布局与按角色读取TextureType是参数化纹理类型 opcode操作数列表刻意很长elementType, shape, isArray, isMS, sampleCount, accessOperand, isShadow, isCombined, format让所有纹理变体收敛到一个 opcode。位置不是约定俗成而是命名常量kCoreModule_TextureShapeParameterIndex1到kCoreModule_TextureFormatParameterIndex8在 core module 与IRResourceTypeBase之间共享slang-ir.h 第 1364-1434 行后者包装为getShapeInst()、getIsArrayInst()、isMultisample()、getAccess()等。判断是否为数组纹理 / 多重采样纹理的 pass 应调用这些访问器而不是手工索引操作数。类似的按角色读取贯穿整个族IRFuncType用getParamCount()/getParamType(i)跳过尾部属性getAttr()单独返回它IRBindExistentialsTypeBase用getExistentialArgCount()/getExistentialArg(i)按角色读取(type, witness)对BoundInterface的min_operands 3形状读作getInterfaceType()/getConcreteType()/getWitnessTable()IRArrayTypeBase::getArrayStride()依据 opcode 选择 stride 索引Array在 2、UnsizedArray在 1IRTaggedUnionType的访问器保证两个集合不被交换。八、验证实操用slangc -dump-ir复现参考文档结论参考文档依赖真实的slangc运行验证gap-intake 报告记录的构建为build-arm64/Debug/bin/slangc版本2026.14.1-80-g6122d03def。你可以用同款命令在自己的构建上复现slangc -dump-ir -target hlsl -entry computeMain ctx.slang -o ctx.hlsl在同时包含[Differentiable] float f与[TreatAsDifferentiable] float g且二者都被bwd_diff调用的 shader 上dump 显示f的 witness table 中同时出现BackwardDiffIntermediateContextType(%f)与BackwardDiffMinimalContextType(%f)g的表中则是Trivial*对另有BackwardDifferentiatePrimal(%f)、BackwardRemat(%f)等函数类型对[BackwardDerivative(h_bwd)]与[BackwardDerivativeOf(k)]的 shader 则产生BackwardContextFromLegacyBwdDiffFunc(%h, %h_bwd_diff)/BackwardMinimalContextFromLegacyBwdDiffFunc(...)。类型 opcode 的可观察形态则由docs/generated/tests/design/ir-reference/types/目录下的门控测试如enum-tag-type.slang、result-type-from-throws.slang、buffer-layout-marker-operands.slang、untyped-resource-handle.slang以filecheck方式固定可作为回归依据。参见ir-reference/types.md——本文所依据的逐 opcode 参考文档主体ir-reference/structure.md——struct、class、interface的容器侧ir-reference/generics-and-existentials.md——BindExistentials、BoundInterface、AnyValueType、DynamicType与*Setopcodeir-reference/resources-and-atomics.md 与 ir-reference/misc.md——消费资源/纹理类型与句柄转换的 opcodeir-reference/differentiation.md 与 ir-reference/decorations.md——微分类型值的构造/投影 opcode 与工作图节点属性ast-reference/types.md——AST 侧Type族与__magic_type/__intrinsic_type之分design/ir.md 与 design/ir-instruction-definition.md——类型化 IR 值的动机、指针相等的类型相等以及 Lua 条目的完整 schema。【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表