news 2026/9/17 10:38:53

Slang 编译器开发实践指南:构建、测试、调试与工程规范(基于 CLAUDE.md)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Slang 编译器开发实践指南:构建、测试、调试与工程规范(基于 CLAUDE.md)

Slang 编译器开发实践指南:构建、测试、调试与工程规范(基于 CLAUDE.md)

【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang

CLAUDE.md 是 Slang 编译器仓库面向开发者(及 AI 编码助手)编写的核心工作手册,系统定义了构建系统用法、CMake 预设配置、测试运行方式、IR 调试工具链、编译器架构总览以及一套严格的"根因修复"工程方法论。阅读本文后,你将掌握如何从零配置 Slang 的构建环境并编译出slangc/slang-test、如何在不依赖 GPU 的情况下编写与运行测试、如何用 IR dump 与 InstTrace 追踪编译器内部问题,以及如何遵守该仓库在 ABI 兼容、PR 描述格式与代码评审上沉淀下来的工程规范。

仓库定位与构建系统

Slang(shader-slang/slang)是 Khronos 主导的 GPU 着色语言项目,主体以 C++ 实现,并配套一种自定义的 Slang 语言作为着色语言本身。CLAUDE.md 明确标注了项目的基本属性:

  • 仓库:shader-slang/slang —— 面向 GPU 编程的着色语言
  • 主要语言:C++ 与自定义 Slang 语言

平台化构建入口

在 Windows 沙箱环境中,推荐直接运行 extras/win-sandbox-build.bat。该脚本会自动发现 Visual Studio、执行vcvarsall.bat、使用vs2022-dev预设配置,并优先使用本地缓存的依赖而非联网拉取,默认构建slangcslang-testslangi三个目标,也可传入额外目标名覆盖默认集合。

在非 Windows 平台(Linux/macOS)上,直接用 CMake 构建。仓库根目录的 CMakePresets.json(版本 6,要求 CMake ≥ 3.25)定义了一套预设体系,其中default预设使用Ninja Multi-Config生成器,输出目录为${sourceDir}/build,并预设了Debug;Release;RelWithDebInfo;MinSizeRel四种配置:

# 使用默认设置配置(Ninja Multi-Config) cmake --preset default # Windows 上优先使用 VS2022 预设。 # -DSLANG_IGNORE_ABORT_MSG=ON:抑制无人值守/LLM 驱动构建中的模态中断对话框 # -DSLANG_EMBED_CORE_MODULE=OFF:将 core 模块编译与 C++ 编译解耦, # 使 *.meta.slang(如 hlsl.meta.slang)的错误不会中断 C++ 编译, # 而是由独立的 slang-bootstrap -compile-core-module 步骤报告。 cmake.exe --preset vs2022 -DSLANG_IGNORE_ABORT_MSG=ON -DSLANG_EMBED_CORE_MODULE=OFF # 构建 Release/Debug 二进制,耗时取决于机器,约 5 到 20 分钟 cmake --build --preset debug # Debug 二进制 cmake --build --preset release # Release 二进制 # 或者用 workflow 预设一步完成 configure + build cmake --workflow --preset debug # 构建特定目标 cmake --build --preset debug --target slangc cmake --build --preset debug --target slang-test

两个值得注意的 CMake 选项:

  • sccache:配置时传-DSLANG_USE_SCCACHE=ON(或设置环境变量SLANG_USE_SCCACHE=1)即可把 sccache 用作编译器启动器以加速增量构建;它会自动禁用预编译头(二者存在已知不兼容),且要求sccache在 PATH 中。在 CMakeLists.txt 中可以确认该选项同时支持命令行开关与环境变量两种形式。
  • SLANG_IGNORE_ABORT_MSG:该 CMake 选项控制 Windows 上异常抛出后的行为,CLAUDE.md 建议 LLM 工作流下强烈开启,因为它在编译期就把行为固化进所有可执行文件。

对于自动化或 LLM 驱动的构建,CLAUDE.md 还给出了一条省 token 的技巧:首次构建把全部输出重定向到空设备,仅在失败时重跑并打印日志:

# 仅在初次尝试失败时才打印构建日志 cmake --build --preset debug >/dev/null 2>&1 || cmake --build --preset debug

格式化与代码风格

提交前必须运行./extras/formatting.sh,PR 必须符合项目代码风格;用./extras/formatting.sh --check-only可以只校验而不修改文件。extras/formatting.sh 支持按语言过滤(--cpp--yaml--md--sh--cmake)、只格式化相对某个 revision 的变更(--since <rev>)、只处理工作区修改过的文件(--modified),并要求 Bash ≥ 3.2(兼容 macOS 自带的/bin/bash)。格式规则遵循.clang-format.editorconfig:四空格缩进、Allman 大括号、100 列限制、左对齐指针、文件末尾换行。

抑制未使用变量警告的惯用写法

if条件中声明的变量在条件体内不被使用(条件本身只起类型检查的副作用)时,应使用C++17 if-init-statement写法,而不是SLANG_UNUSED

// 推荐:C++17 if-init 模式 if (auto foo = as<IRFoo>(inst); foo) { // foo 在体内用不到 —— 类型检查本身就是目的 } // 避免:在体内写 SLANG_UNUSED if (auto foo = as<IRFoo>(inst)) { SLANG_UNUSED(foo); }

对于"被赋值但从不读取"的普通局部变量,则使用带注释说明理由的SLANG_UNUSED(var)

问题解决方法论:走"原则之路"而非最小改动

CLAUDE.md 用相当篇幅规定了该仓库的调试哲学——遵循 principled path(原则性路径),而非 minimal-edit-distance path(最小编辑距离路径),目标是构建出"结构上正确、天然健壮"的表示,即使代价是更大的重构:

  • 修根因,不修症状。一个出现在 emit/codegen 的 bug,根因通常在上游——某个 IR pass、类型合法化、特化、lowering,或 AST/IR 表示本身。要追到那一层去修。
  • 质疑每一次改动。保留一个改动之前先回答:这个改动为什么必要?没有它哪个测试会挂?这是正确的修复,还是问题在暗示你的方向/表示本身有缺陷?说不出"没有它会失败的测试",这个改动大概率不该存在。
  • 不要用补丁掩盖。用守卫、空检查或特判去糊弄一个畸形 AST/IR/witness-table,是掩盖表示层 bug 的创可贴;在正确输入下永远不会命中的守卫就是死代码。优先让表示本身正确,消费者保持简单。
  • 审视输入形状。凡是处理某种输入形状(AST 节点、IR inst、witness、类型)的代码,都要问:这个输入形状本身是否正确、是否原则化?还是应该去修上游生产者?如果形状是错误或偶然的,修生产者;只有形状确实属于合法输入时才在本地处理。这个答案必须写进 PR 描述(见下文 Process report)。
  • 正确的表示优先于编辑距离。如果两种表面形式本应等价,就应该用相同方式建模;如果消费者按位置/索引/身份读取概念上无序的 key→value 数据(如 witness-table / 接口需求条目),就应改成按角色/键访问。
  • 全程维护工作日志。用一份临时 markdown 记录问题与动机示例、遇到的阻塞、问题如何级联(一个修复暴露下一个)、每个修复的选择与"为什么它原则化"(附具体代码追踪)、被否决的替代方案及原因。这份日志是 PR 描述的素材,但本身不进入提交。

对"不原则化改动"的自检

在完成一个非平凡的编译器改动之前,要审查 diff 中是否存在"在替糟糕的 AST/IR/Val/witness 表示买单"的迹象。以下模式在你能证明层次正确之前一律视为危险信号:

  • 自定义语义等价:新写的对DeclRefValTypeWitness或 IR 形状的递归辅助函数(如are...Equivalentdoes...Matchtry...Match)往往意味着两套本不该并存的表示。先问:为什么substituteresolvegetCanonicalTypeequals或既有的规范化构造器不能直接让两个值相等?
  • 未经审计的辅助函数膨胀:每个新 helper、fallback、try...函数都是审查对象。只为让一个失败测试通过、或重复实现了替换/解析/AST 拷贝/泛型求解/查找/lowering 一部分逻辑的 helper,往往就是藏不原则化修复的地方。
  • 从语义到语法的重建:把已检查的语义数据(ValTypeDeclRef、witness、lowered IR 值)再翻译回语法(ExprTypeExp、parser 形状的 AST)是强烈异味——已检查的语义字段才应该是 source of truth。
  • 靠图遍历重新发现上下文:遍历任意操作数图、替换链、witness 链、查找路径或 IR users 来恢复泛型参数、需求键、规范路径或父声明,通常是下游补救。应在生产者侧直接存储或构造规范形式。
  • 消费者侧打补丁:lowering、emit、特化、typeflow 与后端代码不应去修补早期阶段产生的畸形 AST/IR 形状;如果它们需要知道前端的偶然表示细节,应追溯并修复生产者。
  • 硬编码的表示细节:针对特定DeclRef子类、内建魔数类型名、泛型参数索引、witness-table 条目顺序、嵌套 vs 扁平特化形状的特判,需要强不变量支撑,且通常应放在规范化构造边界。
  • 静默处理不可能形状:对超出契约的形状静默返回默认值会藏 bug。应断言不可能形状;只有能说清"为什么这是合法输入"时才处理。

自检从"可疑 helper 清单"开始:列出每个新 helper/fallback/特判、它覆盖的既有机制、没有它会失败的测试,以及它最终幸存/被回滚/被生产者侧修复取代。对每个被标记的改动执行审计:指出确切的输入形状与生产函数、判断形状是规范还是偶然、先尝试生产者侧修复、指明已存在的语义 source of truth、指名没有该改动会失败的测试,并做"revert drill"(删掉 helper,跑最小失败测试,用失败信息追踪真正的生产者-消费者断点)。不要仅仅因为某改动让测试通过就保留它;如果确有必要,PR 的 Process report 必须论证该输入形状为何合法、该层为何拥有这段逻辑,并给出从生产者到消费者的代码追踪。

代码风格与评审约定

这些是从反复出现的评审反馈中提炼的规则,遵守它们可以避免评审往返(它们管"代码如何读、如何组织",与上文方法论管"改什么"相区分):

  • 函数注释写成完整句子:先 what,再 why。先说函数做什么;若存在理由不明显,再简短说明为什么。非平凡行为要附具体示例。例如substituteElementOfCompositeType应写成"Returntargetwith its element type replaced bynewElementType, preserving shape: scalar → newElementType,vector<T,N>vector<newElementType,N>,matrix<T,R,C>matrix<newElementType,R,C>."而不是 "element coerce target"。
  • 用对话式示例写注释与 PR 说明。解释微妙编译器路径时,先写 "Consider this example:" 再贴相关用户代码;不要用 "Full source shape"、"AST trace"、"IR trace" 之类的抽象标签代替解释。代码之后用自然语言逐步说明:哪个 parser、checker、copier、lowering pass 或 IR pass 创建了该形状,本地代码保持什么不变量,哪个下游消费者依赖它。
  • 先复用再新写;把非平凡逻辑提取为具名、有文档的 helper。写新 helper 前先搜共享头文件——AST/IR helper 在slang-ast-type.hslang-ir-util.h及各个*-util.h里。例如判断某类型是否为特定声明的DeclRefType,应使用现成的isDeclRefTypeOf<T>(type)。真正新的逻辑不要埋进 inline lambda 或长内联块,要给出表达意图的名字(如coerceOperandsOfBuiltinBinaryExprsubstituteElementOfCompositeTypeunifyBaseType)和文档注释。
  • 保持单一 source of truth;重构后删除死代码。一个映射/分类只放在一处——例如"操作符名 → operation kind"的映射只存在于getBuiltinOperationKindFromString,不在调用点重复实现。改动使某分支/fallback/helper 不可达后就删掉。
  • 每个值只有一种规范表示;断言不变量。不要为已有表示的东西引入第二种 AST/IR/Val表示——同一逻辑值的多种形态会破坏equals/恒等检查与去重。当不变量保证某些输入永不产出某表示时(如+/-/*永远是PolynomialIntVal,绝不会是BuiltinOperationIntVal),在构造点用SLANG_ASSERT断言,让违规当场暴露。
  • 对超出契约的输入大声失败。helper 只对受限输入集有效时,对集合外的输入用SLANG_RELEASE_ASSERT,而不是静默返回默认值——例如substituteElementOfCompositeType就断言其操作数必须是内建 scalar/vector/matrix。

PR 工作流与提交规范

  1. 格式化:提交前运行./extras/formatting.sh
  2. PR 标签:默认用 "pr: non-breaking";ABI/语言破坏性改动用 "pr: breaking change";
  3. 附带测试:回归测试以.slang文件放在tests/下;
  4. PR 描述必须遵循五段式格式
    1. Motivation—— 要解决的问题,附具体示例/动机测试用例;
    2. Proposed solution—— 方案与"为什么它是原则化选择";
    3. Change summary—— 触及的文件/区域表格或列表;
    4. Concepts and vocabulary—— 置于 change summary 与 process report 之间的短词汇表,只重述 codebase 特有或微妙的术语(如 witness /getSub、facet /getInheritanceInfo、fixpoint solver),不解释interface、associated type 这类常识;
    5. Process report—— 用逻辑理由解释每一处改动;对级联问题要给出动机测试用例与代码追踪(确切涉及的函数/inst),而不仅是描述;对任何处理、守卫或特判特定输入形状的改动,必须回答方法论中的"输入形状检查"——该形状是否正确原则化,还是应修生产者——让评审者能确认修复位于正确的层。

写作要求:面向脑中不持有完整上下文的评审者,采用与代码注释相同的对话式风格——从具体用户代码示例出发,贴完整相关片段而非只报类型/函数名,按顺序解释逻辑步骤,说明编译器构建了什么、该表示如何流经具名函数或 IR 指令、为什么选定的修复保持了不变量。

提交信息另有一条约定:不要在 commit message 中提及 Claude。配套的 AGENTS.md 补充了提交主题用短祈使句(如Reject invalid descriptor heap access)、PR 保持小颗粒且基于master、格式失败可运行./extras/install-git-hooks.sh安装的钩子或请求/format等细则。

测试体系

slang-test必须从仓库根目录运行:

# 多 server 并行跑全部测试(10 到 30 分钟) ./build/Release/bin/slang-test -use-test-server -server-count 8 # 运行单个测试(测试文件必须位于 tests/ 目录之下) ./build/Release/bin/slang-test tests/path/to/test.slang # 运行单元测试 ./build/Release/bin/slang-test slang-unit-test-tool/

无 GPU 环境写测试

  • 用 CPU compute://TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type
  • 用解释器://TEST:INTERPRET(filecheck=CHECK):
  • 参考示例结构见 tests/language-feature/lambda/lambda-0.slang,其中同一用例同时声明了-vk-cpu两条COMPARE_COMPUTE指令,正好演示了"一个测试在 GPU 与 CPU 双路径上验证"的写法:
//TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK):-vk -output-using-type //TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type

诊断测试

诊断系统本身是 Lua 驱动的:诊断定义在source/slang/slang-diagnostics.lua,构建期生成 C++ 结构,完整文档见 docs/diagnostics.md。测试中使用//DIAGNOSTIC_TEST:SIMPLE(diag=CHECK):指令验证编译器发出预期诊断;注释里的注解按消息文本、严重度或错误码与编译器输出匹配,^符号对齐到前一行源码的列:

//DIAGNOSTIC_TEST:SIMPLE(diag=CHECK):-target spirv int foo = undefined; //CHECK: E01234 //CHECK: ^^^^^^^^^ error

SPIR-V 校验

  • 使用slangc -target spirv时设置SLANG_RUN_SPIRV_VALIDATION=1启用静态校验;
  • 不要使用系统自带的spirv-val工具(可能过期),应使用仓库集成的校验路径。

slangc 命令行约定(重要)

Slang 使用单破折号表示多字符选项(不同于大多数工具的--):

  • -help(不是--help
  • -target spirv(不是--target spirv
  • -dump-ir(不是--dump-ir
  • -stage compute(不是--stage compute

同时 CLAUDE.md 明确要求避免以下无人维护、不可靠或不必要的调试选项:slangc 的-dump-ast-dump-intermediate-prefix-dump-intermediates-dump-ir-ids-serial-ir-dump-repro,以及 slang-test 的-category-api

-load-repro-extract-repro是专门用于 repro 处理的工具,仅在处理 repro 机制时使用,输入在使用前会经过校验。

调试工具箱

IR Dump(-dump-ir

# 在每个 pass 处 dump IR(配合 -target 与 -o,避免输出混杂) slangc -dump-ir -target spirv-asm -o tmp.spv test.slang | python extras/split-ir-dump.py # 只 dump 某个 pass 前后 slangc -dump-ir-before lowerGenerics -dump-ir-after lowerGenerics -target spirv-asm -o tmp.spv test.slang > pass.dump
  • 始终将-dump-ir-target(否则编译提前停止)和-o <file>(否则目标代码与 IR 混在 stdout)组合使用;
  • 大 dump 用 extras/split-ir-dump.py 拆分成每 pass 一个文件,工具说明见 extras/split-ir-dump.md;
  • 也可以在 C++ 代码中插入dumpIRToString(),用File::writeAllText()写文件做临时检查;
  • 调试时聚焦 IR pass(特化、内联、类型合法化、buffer lowering)中的根因,而不是在 emit 逻辑里打补丁。编译器的哲学是保持 emit 简单,把重变换都放在 IR pass 里

InstTrace:追踪 IR 指令的来源

给定一个出问题的 IR 指令的 debugUID,可以追踪它是被哪个 pass 创建的:

python3 ./extras/insttrace.py <debugUID> ./build/Debug/bin/slangc tests/my-test.slang -target spirv

脚本为 extras/insttrace.py。

SPIRV 相关工具

  • slangc -target spirv-asm:编译到 SPIR-V 汇编;
  • SLANG_RUN_SPIRV_VALIDATION=1开启静态校验;校验失败时加-skip-spirv-validation仍可看到 SPIR-V 输出;
  • slangc -target spirv-asm -emit-spirv-via-glsl:经 GLSL 生成参考 SPIR-V 用于对比。

断言行为(SLANG_ASSERT

Windows 上断言失败默认弹出阻塞执行的模态对话框。用环境变量SLANG_ASSERT控制:

取值行为
system使用系统assert(),弹出模态对话框并允许附加调试器
debugbreak调试器已附加时触发 debug-break;未附加时回退到system行为
release-assert-only跳过 debug-only 断言(SLANG_ASSERTSLANG_ASSERT_FAILURE)继续执行;SLANG_RELEASE_ASSERT仍然触发
未设置抛出异常

Windows 上异常抛出后的行为由 CMake 选项SLANG_IGNORE_ABORT_MSG控制,对 LLM 无人值守工作流强烈推荐(该选项在编译期把行为固化进所有构建产物,见 CMakeLists.txt)。

架构总览

编译器管线核心组件

  • Lexer(source/compiler-core/slang-lexer.cpp):词法分析
  • Preprocessor(source/slang/slang-preprocessor.cpp):处理#include、宏、条件编译
  • Parser(source/slang/slang-parser.cpp):递归下降解析,产出 AST
  • Semantic Checker(source/slang/slang-check.cpp):类型检查、名字解析、语义验证
  • IR Generation(source/slang/slang-lower-to-ir.cpp):AST 到 Slang IR 的转换
  • IR Passessource/slang/slang-ir-*.cpp):优化与 lowering
  • Code Emissionsource/slang/slang-emit-*.cpp):目标相关的代码生成

关键目录

目录职责
source/core/核心工具(字符串、容器、文件系统、平台抽象)
source/compiler-core/编译器基础设施(诊断、下游编译器集成)
source/slang/主编译器实现(前端、IR、后端)
source/slangc/命令行编译器工具
source/slang-core-module/source/slang-glsl-module/source/standard-modules/标准库模块
source/slang-wasm/WebAssembly 绑定
source/slang-record-replay/API 调用录制/回放
source/slang-rt/运行时库
tools/开发与测试工具
include/公共 API 头文件(slang.h
external/第三方依赖与子模块
prelude/内建语言定义与标准库
tests/综合测试套件
docs/项目文档(用户指南在docs/user-guide/
build/source/slang/fiddle/FIDDLE 宏在构建期生成的代码

编译模型的关键概念

  • CompileRequest:捆绑选项、输入文件与代码生成请求;
  • TranslationUnit:源文件集合(HLSL 每文件一个,Slang 全部文件合并);
  • EntryPoint:函数名 + 要编译的管线阶段;
  • Target:输出格式(DXIL、SPIR-V 等)+ 能力配置。

支持的目标:Direct3D 11/12(HLSL 输出)、Vulkan(SPIR-V、GLSL 输出)、Metal(MSL,实验性)、WebGPU(WGSL,实验性)、CUDA/OptiX(C++ 输出)、CPU(C++ 输出,可执行文件/库)。

IR 系统与生成代码

  • Slang 使用自研的 SSA IR(不是 LLVM);IR 指令在slang-ir-insts.h中定义,该头文件由 Lua 生成(定义在 source/slang/slang-ir-insts.lua);
  • 有完善的 IR pass 框架用于优化与 lowering,代码生成前执行目标相关的合法化 pass;
  • kIROp_开头的枚举值定义在生成文件build/source/slang/fiddle/slang-ir-insts-enum.h.fiddle中;
  • AST 节点声明中的FIDDLE()语句表示会从build/source/slang/fiddle生成并包含额外源码,提供静态类型系统/反射元数据、visitor 支持与序列化支持。

此外还有:Language Server(source/slang/slang-language-server.cpp,支持 IntelliSense、补全、诊断、格式化,供 VS Code/Visual Studio 扩展使用);模块系统(支持单独编译、模块可编译为 IR 并在运行时链接、可对分发的模块做可选混淆、核心语言特性以prelude/下的模块形式定义)。

常见开发任务

添加新语言特性的六步流程

  1. 为新 token 更新 lexer(source/compiler-core/slang-lexer.cpp
  2. 扩展 parser(source/slang/slang-parser.cpp
  3. 添加语义分析(source/slang/slang-check-*.cpp
  4. 实现 IR 生成(source/slang/slang-ir-*.cpp
  5. 为每个目标后端添加代码生成(source/slang/slang-emit-*.cpp
  6. tests/下编写完备测试

其他常见任务:

  • 新增 IR 指令:更新source/slang/slang-ir-insts.lua定义文件后重新生成;
  • 新增内建函数:加到prelude/中相应模块;
  • 新增目标:在source/slang/slang-emit-*.cpp中实现新 emitter。

Capability Atoms 文档生成(勿手改)

docs/user-guide/a4-02-reference-capability-atoms.md 是自动生成的,永远不要直接编辑。它由slang-capability-generator(源码在 tools/slang-capability-generator)从 source/slang/slang-capabilities.capdef 产生。更新某个 capability atom 的描述:

  1. slang-capabilities.capdefdefalias前紧邻处添加/更新///文档注释:

    /// My description here. alias myatom = ...;
  2. 重新生成文档:

    cmake --build --preset debug --target slang-capability-generator mkdir -p build/capgen-out ./build/generators/Debug/bin/slang-capability-generator \ source/slang/slang-capabilities.capdef \ --target-directory build/capgen-out \ --doc docs/user-guide/a4-02-reference-capability-atoms.md
  3. 把更新后的slang-capabilities.capdef与重新生成的.md一起提交。

注意:///注释必须写在公共 alias上(如alias node = _node;),而不是内部def _node : stage;atom 上,否则描述不会出现在公共名字下。

修改公共头文件(include/):ABI 兼容性铁律

include/下所有文件都是公共 API,改动必须对"针对旧版头文件编译的调用方"保持二进制(ABI)与源码兼容。

枚举:

  • 绝不能在现有枚举中部插入新成员——插入会移动其后所有整数值,静默破坏任何存储或比较该值的调用方;
  • 始终追加到终止计数/哨兵成员(如CountOfCountNUM_*)之前,并赋显式整数值(前一成员之后的下一个连续整数);
  • 删除成员:改名为REMOVED_<Name>并保留原整数值,永不复用已退役的整数。

虚表(COM 接口):Slang 的公共接口(ISessionIModuleIComponentType等)是在 include/slang.h 中以virtual方法声明的 COM 风格虚表,虚表布局由声明顺序固定。违反以下规则会损坏虚表,导致针对旧头文件编译的调用方静默崩溃或派发错方法:

  • 绝不重排接口内的虚方法;
  • 绝不改变虚方法签名(返回类型、参数类型、调用约定或SLANG_MCALL修饰);
  • 绝不在接口中部插入新虚方法——只允许在接口末尾、右花括号之前追加;
  • 绝不删除虚方法——把方法体替换为返回SLANG_E_NOT_IMPLEMENTED的 stub,声明保留原位;
  • 若客户端可能按 UUID 实现或查询某接口,避免就地扩展现有公共 COM 接口;优先新增带自己 UUID 的派生/版本化接口,同时保持原接口声明与 UUID 继续受支持。

修改 core 模块源(hlsl.meta.slang/core.meta.slang)后的重建

core 模块源码(source/slang/hlsl.meta.slang、core.meta.slang等)在编译期被嵌入slang-bootstrap二进制。修改这些文件后,需要让 CMake 感知新的时间戳、经构建图重新生成 core-module 头文件、再重编slangc

cmake -E touch source/slang/hlsl.meta.slang # 或实际改动的 meta 文件 cmake --build --preset <preset> --target generate_core_module_headers cmake --build --preset <preset> --target slangc

<preset>与你实际使用的构建一致(debugreleasereleaseWithDebugInfo等)。从 source/slang-core-module/CMakeLists.txt 与 source/slang/CMakeLists.txt 的构建图可以看到:generate_core_module会调用对应宿主/配置的slang-bootstrap完成引导编译,generate_core_module_headers依赖它,而slangcREQUIRES generate_core_module_cache(一个 ALL 目标)——所以即使只构建--target slangc也会触发该步骤。若跳过cmake -E touch这一步,缓存中的 bootstrap 二进制可能静默嵌入旧源码,bootstrap 步骤的诊断与当前源文件对不上,即是二进制过期的明确信号。

HLSL 命名常量输出规则

绝不把 HLSL 枚举/命名常量的值以硬编码整数输出。DXC 在解析期映射命名常量(属性字符串、标志标识符等);一旦把数字烘焙进生成的 HLSL,而 DXC 日后内部映射发生变化,输出会静默失效。正确模式:

  1. 为每组概念值定义一个 Slang 枚举(或一组由内建支撑的命名常量,如NodeLaunch模式、Barrier 标志集);
  2. IR 中存名字而非整数:使用IRStringLit操作数(如NodeLaunchDecoration的做法),或保留标识符到输出阶段的Ref<T>/内建访问器;
  3. 在 HLSL emitter(slang-emit-hlsl.cppslang-emit-c-like.cpp)中提供映射函数,把存储的枚举/字符串值转回 HLSL 源名,使输出形如[NodeLaunch("broadcasting")]而非[NodeLaunch(0)]

代码库中已有该模式的实例:NodeLaunchDecorationIRStringLit("broadcasting")存储模式、emitter 原样重发字符串;work-graph 输出记录的Get()返回由__intrinsic_asm ".Get"支撑的Ref<T>,使输出 HLSL 得到.Get(i)(HLSL 中的 l-value)而非整数偏移。

若一个 Slang 枚举必须以命名常量而非整数形式输出(如UAV_MEMORY而不是1),按如下五步:

  1. 把 C++ 枚举定义在slang-type-system-shared.h中(namespace Slang内,用普通enum而非enum class,使值可隐式转int;该头被 core-module 源码与 emitter 共同包含);

  2. 在相应*.meta.slang中镜像为 Slang 枚举,用$(...)拼接从 C++ 取值,保持两边同步:

    enum MyFlags : uint { FlagA = $(MyFlags::FlagA), FlagB = $(MyFlags::FlagB) }
  3. .meta.slang中声明__intrinsic_op转换器,在 IR 中表示"枚举到字符串"的转换——传给__intrinsic_op(...)的助记符必须与第 4 步 Lua 键完全一致:

    __intrinsic_op(getEnumMyFlags) int GetEnumMyFlags(MyFlags f);
  4. 在 source/slang/slang-ir-insts.lua 注册新 IR op,并在slang-ir-insts-stable-names.lua添加稳定 ID;

  5. 在目标 emitter(如slang-emit-hlsl.cpptryEmitInstExprImpl)中输出命名常量字符串:IR 操作保持绑定到符号化枚举或内建值,把每个被接受的位/值映射到其 HLSL 命名常量字符串后用m_writer->emit(...)写出。不要实现或记录"从原始整数位置反推 HLSL 源名"的方案。

跨平台注意事项

  • 支持平台:Windows(x64/ARM64)、Linux(x64/ARM64)、macOS(x64/ARM64)、WebAssembly;
  • 平台抽象:文件系统、进程管理、平台检测统一使用source/core/中的工具;
  • 图形 API:代码生成支持所有主流 API,但运行时测试需要相应驱动/SDK;
  • WSL on Windows:在 WSL 环境下运行,尽量给可执行文件追加.exe以避免误用 Linux 二进制——用cmake.exe而非cmakepython.exe而非pythongh.exe而非gh。配套的 AGENTS.md 进一步细化:WSL 下默认使用 Windows 原生工具(git.exe等,因为 WSL Git 可能损坏 worktree 状态),跨文件系统传路径用wslpath -w/wslpath -u转换;原生 Linux/macOS 上则直接使用无.exe后缀的平台工具。

延伸阅读

  • 用户面向文档:docs/user-guide/(语言规范见仓库外部 spec 仓库,按 CLAUDE.md 指示克隆到external/后位于external/spec/specification/,特性提案在external/spec/proposals/
  • 诊断系统完整说明:docs/diagnostics.md
  • 代码规范:docs/design/coding-conventions.md
  • 通用构建参考:docs/building.md
  • 编译器 IR 设计:docs/design/ir.md

CLAUDE.md 的价值不仅在于罗列命令,更在于它把一套"表示层正确性优先"的编译器工程文化——从 C++17 if-init 的小技巧到 PR 五段式描述、从 ABI 铁律到"revert drill"式自检——沉淀为可执行的规则。对任何参与 Slang 编译器开发或希望为大型 C++ 编译器仓库配置 AI 协作规范的团队,这份文档都是高参考价值的范本。

【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang

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

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

LeRobot 仿真实战:如何 30 分钟跑通策略训练并落到真机

LeRobot 仿真实战&#xff1a;如何 30 分钟跑通策略训练并落到真机 【免费下载链接】lerobot &#x1f917; LeRobot: Making AI for Robotics more accessible with end-to-end learning 项目地址: https://gitcode.com/GitHub_Trending/le/lerobot 面向能跑 bash 的读…

作者头像 李华
网站建设 2026/9/17 10:35:33

电动车路径优化:MOPGA-NSGA-II混合算法在Matlab中的实现

1. 项目背景与核心挑战电动车路径规划问题在近年来越发受到学术界和工业界的关注。不同于传统燃油车&#xff0c;电动车在行驶过程中需要额外考虑充电站布局、充电时间、电池衰减等特殊因素。特别是在复杂城市环境中&#xff0c;路况变化、天气影响以及充电设施分布不均等问题&…

作者头像 李华
网站建设 2026/9/17 10:34:51

嵌入式Linux学习路线:从裸机到驱动的硬核闭环

1. 这条学习路线不是“学完就能上岗”&#xff0c;而是帮你避开三年才醒悟的弯路我带过27个嵌入式Linux方向的新人&#xff0c;从应届生到转行程序员&#xff0c;平均入职前自学时长14.6个月。其中19个人在第8~12个月卡住——不是不会写代码&#xff0c;而是根本不知道自己该学…

作者头像 李华
网站建设 2026/9/17 10:33:13

内存态匿名段分析实战:绕过DexProtector检测并还原Dex

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

作者头像 李华