拓冰建站拓冰建站
首页 / 资讯中心 / 正文

scriptc 的 LLVM 原生代码生成助手(llvm-codegen):架构、构建与协议全解析

编译器语言运行时开发工具CLI【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址https://gitcode.com/GitHub_Trending/sc/scriptc点击查看免费下载本指南围绕 scriptcTypeScript-to-Native Compiler仓库中 native/llvm-codegen/README.md 展开深入剖析这个进程外out-of-processLLVM 代码生成助手的职责边界、构建方式、CLI 协议与发射流水线。读者将掌握为什么 scriptc 需要独立的 LLVM helper 而编译器不搜索PATH、如何为 macOS arm64 手工构建该发布产物、version/emit两个子命令的完整参数语义以及源码层面优化前验证 → 默认 O2 流水线 → 优化后验证 → 原子发布的可靠性设计。一、为什么需要一个进程外的代码生成助手scriptc 是 TypeScript 到原生代码的编译器其编译器主体packages/compiler/src运行在 Node.js 进程中而真正的机器码汇编/目标文件发射能力由 LLVM 提供。两者之间存在明显的技术边界差异ABI 与链接差异LLVM 官方发行版通常以静态库形式提供且构建时可能关闭 RTTI直接内嵌进 Node 进程会带来符号冲突、链接模式冲突等一系列问题。平台差异LLVM 二进制发行版对宿主系统libstdc、glibc/musl、macOS 版本敏感与编译器本体解耦后可以按平台独立打包与替换。进程隔离LLVM 的report_fatal_error默认会导致abort()并留下未结构化的崩溃栈。独立进程意味着即使 LLVM 内部出现致命错误编译器Node 调用方也能收到结构化的 JSON 诊断而不会直接崩溃。因此仓库将 LLVM 汇编与目标文件发射封装为一个专门的、版本化的小协议进程外助手scriptc-llvm-codegen。它只做一件事——接收 LLVM IR 文件与一组参数输出.o目标文件或.s汇编文件全部交互通过标准输入/输出完成。这一设计也直接呼应了 CMakeLists.txt 中对 RTTI 模式的显式处理官方 Linux 和 Windows LLVM 归档通常禁用 RTTI混用 RTTI 模式会把普通的 virtual typeinfo 变成未解析的链接符号。二、版本矩阵LLVM 22.1.8、AArch64 后端与 macOS 15/14 双目标README 明确了该助手的关键版本事实源码在多个位置与之呼应维度值出处绑定的 LLVM 版本精确22.1.8find_package(LLVM 22.1.8 EXACT REQUIRED CONFIG)CMakeLists.txt当前编译进去的后端仅AArch64默认SCRIPTC_TARGET_BACKENDSAArch64CMakeLists.txt、target.h默认目标三元组arm64-apple-macosx14.0.0target.h助手可执行文件宿主要求macOS 15Homebrew 固定 LLVM 22 bottle 的最低部署版本CMakeLists.txt发射产物部署目标macOS 14经由 triplearm64-apple-macosx14.0.0README、CMakeLists.txt这里有一个很容易混淆的双版本概念需要重点区分助手可执行文件本身静态链接了 Homebrewllvm22的库而该 bottle 构建于 macOS 15因此打包的二进制最低要求 macOS 15否则无法运行。这在 CMake 中以CMAKE_OSX_DEPLOYMENT_TARGET 15.0强制固化CMakeLists.txt。它发射出的汇编/目标文件则通过 triple 单独瞄准 macOS 14与助手自身的运行环境无关。默认 data layoute-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32由SCRIPTC_DEFAULT_DATA_LAYOUT缓存变量配置CMakeLists.txt。version子命令会把这套矩阵以 JSON 形式如实上报便于编译器在调用前完成身份校验详见下文协议一节。三、构建何时需要、需要什么、如何构建3.1 构建触发条件README 明确指出工作区常规的pnpm -r build不会重建这个发布产物。该助手作为独立的 npm 包发布只有当以下两种情况才需要显式构建正在开发/调试原生发射链路修改 native/llvm-codegen/src 下的 C 源码准备打包发布scriptc/llvm-darwin-arm64。对应地packages/llvm-darwin-arm64/package.json 中的build脚本被刻意设为true空操作真正的原生构建入口是build:native且prepack会先断言bin/scriptc-llvm-codegen可执行防止把未构建的包发布出去。3.2 前置依赖与构建命令在 macOS arm64 上依次执行README 原文命令$ brew install cmake ninja llvm22 $ pnpm --filter scriptc/llvm-darwin-arm64 build:nativeCMake 最低要求 3.24CMakeLists.txt。构建脚本packages/llvm-darwin-arm64/scripts/build.mjs随后会调用 CMake 配置并编译出bin/scriptc-llvm-codegen可执行文件。3.3 可裁剪的 CMake 缓存变量构建可通过 CMake cache 变量定制这些变量同时也是协议如实上报的数据来源Cache 变量默认值作用SCRIPTC_PACKAGE_VERSION0.0.0-dev上报给version子命令的 npm 包版本SCRIPTC_DEFAULT_TARGETarm64-apple-macosx14.0.0默认目标 tripleSCRIPTC_DEFAULT_DATA_LAYOUTarm64 Darwin 布局串默认 data layoutSCRIPTC_TARGET_BACKENDSAArch64分号分隔的后端列表源码已支持AArch64/X86/WebAssembly三个分支见 CMakeLists.txtSCRIPTC_ALLOWED_TARGETSarm64-apple-macosx14.0.0逗号分隔、该助手接受的目标 triple 白名单SCRIPTC_HELPER_ARCHarm64可执行文件的宿主架构SCRIPTC_MUSL_BUILDOFF是否为 Linux musl 构建全静态宿主可执行文件以SCRIPTC_MUSL_BUILD为例LLVM 官方 Linux 归档依赖宿主 libstdc/glibc ABI因此 musl 包把助手构建为全静态宿主可执行文件而其配置的输出目标与运行时包仍保持 muslCMakeLists.txt。该模式下 CMake 会把 LLVM 庞大的静态导入图压平为每个目标的单条绝对归档路径并按依赖方在前的后序逆序组织链接CMakeLists.txt。3.4 针对 Apple 平台的瘦身链接在 Apple 平台上链接选项做了三重收束CMakeLists.txt-exported_symbol,_main只导出进程入口避免把静态归档中的全局 LLVM 符号变成链接根-dead_strip丢弃小协议无法触达的函数与数据使产物跨 LLVM bottle 布局保持稳定-dead_strip_dylibs剔除 Homebrew unversioned LLVM 22 bottle 无条件带入的 Z3 等传递 dylib保证 npm sidecar 在没有 Homebrew 的环境里也能运行。同理CMakeLists.txt 会把zstd::libzstd_shared的导入位置改指静态版本避免 npm sidecar 残留 Homebrew zstd dylib 依赖。四、协议刻意保持小而带版本号README 强调协议intentionally small and versioned。它只有两个子命令协议版本号在 target.h 中定义为字面量ProtocolVersion 1由编译期宏注入llvm_version等信息CMakeLists.txt。4.1version身份与能力上报scriptc-llvm-codegen version --formatjson从 main.cpp 可以看到version命令严格要求--formatjson否则报usage错误返回的 JSON 对象包含protocol_version协议版本1scriptc_package_versionCMake 注入的包版本llvm_versionLLVM_VERSION_STRING即 22.1.8host_triplesys::getDefaultTargetTriple()得到的宿主三元组targets编译进去的后端列表解析SCRIPTC_TARGET_BACKENDSsupported_targets白名单 triple 列表解析SCRIPTC_ALLOWED_TARGETSdefault_target与data_layout来自默认 target machine 的真实布局字符串。编译器在加载助手时会先调用version做身份探测identity check并把二进制内容与包清单纳入指纹快照验证通过后才允许进入发射流程见 native-codegen.ts。4.2emit发射汇编或目标文件scriptc-llvm-codegen emit --input app.ll --output app.o --filetype obj \ --target arm64-apple-macosx14.0.0 --opt-level 2 \ --relocation-model pic --diagnostic-format json --source-path app.ts参数语义全部由 emit.cpp 中的parseEmitOptions与后续校验实现整理如下参数必选取值/默认校验与源码依据--input是输入 LLVM IR 文件路径缺失直接返回usage错误--output是输出文件路径同上--filetype否obj默认或asm非二者报invalid_filetype--target否默认arm64-apple-macosx14.0.0必须命中supported_targets否则报unsupported_target--opt-level否0\|1\|2\|3\|s\|z默认2非枚举值报invalid_opt_level--relocation-model否仅支持pic默认其它值报invalid_relocation_model--diagnostic-format否默认json非 JSON 时错误走纯文本 stderr--source-path否空串即不设置非空时写入 IR 模块的源文件名供调试信息使用注意--opt-level的取值语义在两个层面生效优化流水线层面emit.cpp0→O0、1→O1、2→O2、3→O3、s→Os、z→Oz默认返回O2代码生成层面target.cpp0→CodeGenOptLevel::None、1→Less、3→Aggressive其余2/s/z统一映射到Default。此外target.cpp 的createTargetMachine固定使用Reloc::PIC_与CodeModel::Small——这正是--relocation-model pic被硬性约束为唯一选项的底层原因。五、发射流水线从 IR 到目标文件的七步走完整流程实现在 emit.cpp 的emit()函数中可分解为七个阶段参数校验依次检查 target、filetype、opt-level、relocation-model 四类约束前面表格已列。IR 解析用parseIRFile读取--input失败时报invalid_ir并把 SMDiagnostic 渲染进错误信息若给了--source-path则写入Mod-setSourceFileName。TargetMachine 创建createTargetMachine内部先initializeTargets()按编译期宏注册对应后端的 TargetInfo/Target/TargetMC/AsmPrinter见 target.cpp失败报target_machine_failed。IR 归一化与优化前验证**把模块的 triple/data layout 与 TargetMachine 对齐然后verifyModule做合法性验证失败报verification_failed。默认流水线优化通过PassBuilder注册并串联 Loop/Function/CGSCC/Module 四层分析管理器调用buildPerModuleDefaultPipeline按所选优化等级构建LLVM 22 默认的 per-module 流水线——README 特别点名其中包含coroutine lowering协程降低emit.cpp。优化后验证verifyModule再次运行失败报post_optimization_verification_failed确保任何优化器缺陷都能在发射前被拦截。原子发射与发布这一阶段是可靠性的核心——详见下一节。六、原子发布失败或中断不会截断目标产物这是 README 强调的最重要工程细节publishes through a private sibling file so a failed or interrupted request cannot truncate the requested output。实现上emit.cpp在--output旁构造私有 sibling 临时路径output.tmp-%%%%%%通过createUniqueFile获得唯一文件与 fd失败报output_open_failed用legacy::PassManageraddPassesToEmitFile把代码生成写进临时文件目标不支持该 filetype 时报emission_not_supportedflush后检查has_error()有错误报output_write_failed并删除临时文件校验临时文件大小非零空文件报output_verify_failed最后sys::fs::rename把临时文件原子改名为目标路径失败报output_publish_failed。由于写入目标永远发生在rename那一刻之前的任何失败、中断都只会留下一个可清理的临时文件--output要么不存在、要么是完整产物绝不会是半截内容。这一契约被编译器侧的调用方严格依赖见 native-codegen.ts。七、编译器侧集成直接解析 npm 包绝不搜索 PATHREADME 声明the compiler resolves that package directly and never searchesPATHfor this program。这个承诺在 native-codegen.ts 中得到完整落实助手按目标平台从helperPackage字段解析 npm 包如 arm64 Darwin 对应scriptc/llvm-darwin-arm64定义见 targets.ts定位到包内bin/scriptc-llvm-codegenWindows 下为.exe并逐项校验文件存在、可读、可执行模式位检查 access(R_OK | X_OK)否则抛出SC3003missing_package / missing_binary / unusable_binary / identity_probe_failed / invalid_version_response 等细分类进程调用使用execFile不经过 shell也不会触发PATH查找见 native-codegen.ts调用emit时把编译器内部选项映射为协议参数--target取目标平台的llvmTriple、--opt-level取options.optimization ?? 2、--relocation-model取目标的 relocation model、--diagnostic-format固定jsonnative-codegen.ts。除此之外编译器还围绕该协议建立了三道防线输入/输出隔离IR 输入与阶段产物都写在--output的 private sibling 路径native-input、native-kind与最终产物隔离产物校验emit正常退出后仍检查 stage 文件存在、非空否则抛SC3004 empty_output缓存与指纹用snapshotNativeArtifactDependencies对包清单与二进制内容做指纹快照跨调用用nativeArtifactDependenciesStillMatch校验命中后走validCachedFile/installVerifiedCache跳过重复发射并pruneBuildCache清理旧缓存native-codegen.ts。也就是说协议除了本身小还刻意保持可探测、可验证、可缓存编译器总是先问你是谁、支持什么version再带着白名单 triple 去发射emit中途任何异常都以结构化的方式被捕获。八、诊断协议结构化的失败与永不裸崩所有错误统一走 diagnostics.cppreportError(code, message)在--diagnostic-format json下输出{ok: false, code: ..., message: ...}到 stderr返回码1否则输出纯文本scriptc-llvm-codegen: message。各阶段的错误码上文已逐一提及unsupported_target、invalid_ir、verification_failed、post_optimization_verification_failed、output_publish_failed等。更关键的是installFatalDiagnosticHandler()进程启动即注册 LLVM fatal handler把report_fatal_error转成 JSON 诊断后以_Exit(70)退出diagnostics.cpp。配合 main.cpp 中由环境变量SCRIPTC_LLVM_TEST_FATAL触发的自测分支可以验证未来任何 LLVM fatal 都不会变成 Node 调用方看到的裸 abort/栈崩溃。九、总结一个可验证的、版本化的原生发射边界从 native/llvm-codegen/README.md 出发结合 native/llvm-codegen/src 各 C 源文件、CMakeLists.txt、packages/llvm-darwin-arm64/package.json 与 native-codegen.ts可以归纳出这套设计的四条主线边界清晰LLVM 专属构建链CMake、静态归档、RTTI/ABI、dylib 依赖被完全隔离在进程外助手内Node 编译器只需调用两个子命令协议可审计ProtocolVersion 1version自述 白名单 triple 校验任何版本漂移都能在调用前被发现产物可安全发布临时 sibling 文件 rename的原子发布配合优化前后双重verifyModule与空文件检查杜绝截断产物与损坏产物失败可结构化JSON 错误码 fatal handler 编译器侧SC3003/SC3004分类使整条原生链路在 CI 与生产环境均可诊断。对想要深入原生发射链路或为其他平台打包助手的开发者建议按协议 → 构建 → 流水线 → 集成的顺序阅读target.h协议常量与目标白名单→ CMakeLists.txt可裁剪构建→ emit.cpp核心流水线→ native-codegen.ts调用方契约。赞分享编译器语言运行时开发工具CLI【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址https://gitcode.com/GitHub_Trending/sc/scriptc点击查看免费下载相关推荐Mono LLVM 后端深度解析用 LLVM 替代内置 JIT 的代码生成架构与实战指南Mono LLVM 后端深度解析用 LLVM 替代内置 JIT 的代码生成架构与实战指南 Mono 运行时内置了一个自行维护的即时编译器JIT而在 .N语言运行时标准库JIT编译编译器codegen代码生成的智能助手codegen代码生成的智能助手 项目介绍 在软件开发的世界中代码生成和代码转换一直是提高效率、减少人工干预的重要手段。今天我要为大家介绍一个强大的开源项GraalVM Native Image LLVM 后端LLVM Backend完全指南原理、构建、调试与新增目标架构GraalVM Native Image LLVM 后端LLVM Backend完全指南原理、构建、调试与新增目标架构 本文基于 GraalVM 仓库中的编译器JIT编译语言运行时高性能计算内存管理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门