Zig – New SPIR-V Back End Progress

Retro_Dev1 pts0 comments

Devlog<br>Zig Programming Language

Download

Learn

News

Source<br>Join a Community

Zig Software Foundation

Devlog

Devlog

This page contains a curated list of recent changes to main branch Zig.

Also available as an<br>RSS feed.

This page contains entries for the year 2026. Other years are available in<br>the Devlog archive page.

June 26, 2026<br>SPIR-V Backend Progress

Author: Ali Cheraghi<br>There’s quite a bit to cover. The SPIR-V backend had bitrotted in a number of places after the recent compiler changes, so I spent the past several weeks dragging it into a better state.<br>@SpirvType<br>SPIR-V has a handful of types that couldn’t be expressed in Zig’s type system. The new @SpirvType builtin has been introduced to address the longest-standing blocker for writing shaders. See #20550, #23326 and #35461 to trace the background.<br>const Sampler = @SpirvType(.sampler);<br>const Image = @SpirvType(.{ .image = .{<br>.usage = .{ .sampled = u32 },<br>.format = .unknown,<br>.dim = .@"2d",<br>.depth = .unknown,<br>.arrayed = false,<br>.multisampled = false,<br>.access = .unknown,<br>} });<br>const SampledImage = @SpirvType(.{ .sampled_image = Image });<br>const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });<br>const sampled_image = @extern(*addrspace(.constant) const SampledImage, .{<br>.name = "sampled_image",<br>.decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } },<br>});

Execution Mode on the Calling Convention<br>Execution mode info (workgroup size, fragment origin, etc.) is now carried by the calling convention instead of being emitted via inline assembly OpExecutionMode. The old std.gpu.executionMode() helper is gone, and the SPIR-V assembler now rejects manual OpExecutionMode instructions. Two new calling conventions, spirv_task and spirv_mesh, were also added for mesh shading pipelines.<br>export fn vert() callconv(.spirv_vertex) void {}<br>export fn frag() callconv(.{ .spirv_fragment = .{ .depth_assumption = .greater } }) void {}<br>export fn comp() callconv(.{ .spirv_kernel = .{ .x = 8, .y = 8, .z = 1 } }) void {}<br>export fn task() callconv(.{ .spirv_task = .{ .x = 1, .y = 1, .z = 1 } }) void {}<br>export fn mesh() callconv(.{ .spirv_mesh = .{ .stage_output = .output_lines, .max_primitives = 1, .max_vertices = 2 } }) void {}

Capabilities and Extensions from CPU Features<br>Capabilities and extensions used to be emitted ad hoc by codegen or via inline assembly. They’re now driven entirely by the CPU feature set like other targets, with dependency chains extracted from SPIRV-Headers (excluding external vendors for now), and the assembler now rejects any attempt to emit OpCapability or OpExtension directly.<br>Multi-Threaded Codegen<br>From day one, the SPIR-V backend ran codegen single-threaded inside the linker thread. Each codegen job now produces an Mir value just like every other self-hosted backend, and gets scheduled on the compiler’s thread pool.<br>The same change brought back two ISel passes that had been removed during earlier refactors: dedup_types (which merges equivalent type instructions) and prune_unused (which strips dead code from the final module). These had originally been deleted back when codegen was single-threaded.<br>Object File Linking<br>.spv files are now recognised as object files. You can compile multiple .zig files (or external .spv objects) and have the SPIR-V linker stitch them into a single module.<br>Tens of bugs have also been fixed along the way with a nearly 10% increase in total passing behavior tests (49% now) on the spirv64-vulkan target, std.gpu was renamed to std.spirv and the SPIR-V backend is meaningfully more useful than it was a month ago, but there’s still a long way to go. Plenty of behavior tests remain skipped on SPIR-V. That said, if you’ve been on the fence about trying Zig for shaders or compute kernels, this is a good time to give it a shot. Bug reports are very welcome on Codeberg. Happy hacking!

June 25, 2026<br>New @bitCast Semantics and LLVM Backend Improvements

Author: Matthew Lugg<br>(Quite long devlog coming up, apologies—I got a little carried away with this one!)<br>A few weeks ago, I began working on a branch implementing an improvement to the LLVM backend which had been planned for a long time. This ended up snowballing into a bigger change which implemented a few language proposals you might be interested to hear about.<br>LLVM Backend Integer Lowering<br>Zig has always lowered arbitrary bit-width integer types (e.g. u4, i13, u40) directly to LLVM IR’s bit-int types (i4, i13, i40). However, we’ve known for a long time that this lowering is not optimal, because LLVM’s documented semantics for representing these types in memory are unnecessarily restrictive to the optimizer. Perhaps more importantly, because Clang never emits LLVM IR like this, these code paths in LLVM have never been properly tested, and so are poorly supported in practice—over the past few years, we have observed many instances of trivial optimizations being missed and even straight-up miscompilations.<br>So, the original goal of the PR was to only use these bit-int...

spir backend llvm spirvtype const devlog

Related Articles