Expand description
metal2vulkan — Metal AIR (LLVM bitcode) -> Vulkan SPIR-V, via a native LLVM-IR emitter.
The decisive difference from the legacy metal2vulkanspirv crate: that one targeted the OpenCL/Kernel
SPIR-V backend (spirv64-unknown-unknown), which emits UNSTRUCTURED, Physical64/OpenCL-dialect
SPIR-V, then did a ~1300-line regex dialect rewrite + a hand-written structurizer to reach a
Vulkan-legal module. Here the native emitter produces OpCapability Shader / Logical GLSL450
SPIR-V directly from sanitized AIR LLVM IR. What remains is the stage interface — done as a
handful of passes on the crate-owned SPIR-V module representation (see passes/).
Pipeline: .air|.ll -> llvm-dis -> sanitize -> native Vulkan SPIR-V emit -> retained crate
module -> interface+lowering passes -> assemble -> spirv-val (vulkan1.3).
Modules§
- as_
shadow - Host-populated metadata view for AIR acceleration-structure introspection.
- env_
vars - One registry for every
METAL2VULKAN_*environment variable the crate honors (refactor S8). - meta
- AIR stage-interface metadata, parsed from the ORIGINAL
.lltext BEFORE llc runs (llc drops the!air.fragment/!air.vertexmetadata nodes). Each entry-function parameter, in SPIR-VOpFunctionParameterorder (which llc preserves), gets a role telling the interface pass what Vulkan binding to synthesize for it. Ported faithfully frommetal2vulkanspirv::parse_air_fragment_meta, plus a sibling!air.vertexparser the old crate handled structurally rather than from metadata. - native
- Native LLVM-IR -> Vulkan SPIR-V emitter.
- passes
- The residual SPIR-V passes — the stage-interface half of this crate. The structurizer is NOT
here: the native emitter (
native/, with the structured-control-flow logic innative/cfg/andnative/emitter/control.rs) is what turns the AIR CFG into a structuredOpCapability Shader/Logical GLSL450module, emitting OpSelectionMerge/OpLoopMerge and running the budgeted post-hoc repair. By the time a module reaches these passes it is already structured; what remains is purely the stage interface: the emitter lowers each entry parameter to an opaque pointer/value param and eachair.*to an imported function call. The native emitter never emits the legacy LLVM backend’s Linkage capability/decorations. We: - reflect
- Public shader-reflection facade.
- tools
- External-tool runners for metal2vulkan. Production SPIR-V emission is native Rust; the remaining
spawns (
llvm-disandspirv-val) are bounded by a timeout so a stuck tool can’t hang the translator.
Functions§
- canonicalize_
spirv_ bytes - Canonicalize the ids of an emitted SPIR-V byte stream into the deterministic serialized-order form
the shipped pipeline applies in
finish_module. Exposed for the byte-drift gates (historical validation tooling byte-baseline-check/byte-determinism-check`), which compare native-emit output in its canonical (shipped) form rather than the raw emission-order id numbering, which differs benignly between processes and is normalized away here. - detect_
stage - Translate an AIR/
.llshader to Vulkan SPIR-V bytes for the given stage. On success returns the assembled SPIR-V words. The caller validates withtools::spirv_val. Detect the shader stage from the AIR’s own!air.vertex/!air.fragment/!air.kernelmetadata (which the compiler emits and SPIR-V emission later drops). Lets the caller translate a captured AIR blob without knowing its stage — the kext forwards raw AIR; the stage is intrinsic to the module. A fragment shader run as--stage vertex(or vice-versa) mis-maps the [[position]] role, so detecting beats guessing. - disassemble
- Disassemble SPIR-V bytes to spvasm text (for golden fixtures / debugging).
- specialize_
function_ constants - Bake explicit values into a module’s Metal function constants, in place on assembled SPIR-V.
- translate
- translate_
bda_ probe - Diagnostic probe: emit the BDA device-pointer tier (
emit_vulkan_spirv_all_buffers_raw_bda) for a case and run it through the samefinish_modulefinalization the productionbda_retryapplies, so the surviving spirv-val residual of araw store for Ptr(1)BDA case can be inspected. Mirrorstranslate_raw_tiers_probe; not a frontier signal. - translate_
native_ no_ retry - Emit + passes transform for a sanitized AIR module, stopping BEFORE spirv-val and the retry
cascade — the byte-drift-check (BC) gate’s translate path. Returns the canonical SPIR-V bytes the
default path would hand to spirv-val on success (
Ok), or the emit/passes error (Err). No subprocess and no retry tiers: this is exactly the “primary emit path + passes layer” BC regression-guards, and it is byte-identical to the pre-spirv-val stage oftranslate_sanitized_native(the same async-copy lowering, meta parse,emit_vulkan_spirv, andfinish_modulethe default path runs before it validates). The retry cascade only fires when this output fails spirv-val, so BC deliberately does NOT see it (retry output needs spirv-val to gate, which the milestone battery’s G4/G5 cover). - translate_
native_ primary_ validated - Return the default-path-aligned PRIMARY candidate without entering the retry cascade. This is
intentionally separate from
translate_native_no_retry: the byte baseline must stay an in-process, pre-validator gate, while a cross-binding pointer phi can only be adopted after spirv-val proves that it is the actual failing rule. Callers that measure the primary-validity floor use this form with a per-worker temporary directory. - translate_
passthrough - Generate a fullscreen-triangle vertex shader whose output interface matches a fragment shader’s
[[stage_in]]inputs. Used when APV pipelines bind a built-in vertex slot rather than AIR vertex code. - translate_
pre_ psb_ probe - Diagnostic probe (used by
historical PSB dump probes): run the exact production translate path but with the W1 PhysicalStorageBuffer64 retry tiers DISABLED, returning the pre-PSB emission the cascade would otherwise feed to the PSB rewrite. Lets the--psb-dumptool applyrewrite_cross_binding_pointer_merges_bytesby hand for inspection without an env flip (the retiredMETAL2VULKAN_PSB=0). Not a frontier signal; production always runs with PSB on. - translate_
raw_ tiers_ probe - Probe-only (used by
historical raw-residual probes): emit the two R4 raw-retry tiers for a module and return each tier’s emitted+assembled SPIR-V bytes (Ok) or its emit error (Err), through the EXACT samefinish_moduletransform the production retry applies. The caller spirv-vals each to measure the TRUE residual blocker once raw byte-offset modeling resolves the buffer pointer typing: a pointer-merge-classified case whose raw tier emits clean but is then rejected for a CFG/other reason is MULTIPLY-BLOCKED (R4 + R2/…), not a pure R4 case. Returns[tier1, tier2]where tier1 = device/constant buffers raw, tier2 = + threadgroup buffers raw. - translate_
reflected - Like
translatebut also returns thereflect::ShaderReflection— the stage-interface facade (descriptor bindings, vertex attributes, varyings, render targets) the translator already parsed, so a downstream consumer never re-reflects the emitted SPIR-V. The SPIR-V bytes are byte-identical totranslate; reflection is a pure re-shaping of the parsed metadata. - translate_
reflected_ with_ options - translate_
sanitized_ native - Translate already-sanitized LLVM IR through the native emitter. The LLVM
llcbackend and its crash-workaround passes are not used. - translate_
sanitized_ native_ reflected translate_sanitized_native_with_optionsplus the reflection facade. Seetranslate_reflected.- translate_
sanitized_ native_ with_ options - translate_
with_ options