From 495c65d5140a1907575b696f2a713dfedde5c047 Mon Sep 17 00:00:00 2001 From: Brian Ferri Date: Thu, 1 Oct 2026 21:14:52 +0200 Subject: [PATCH 1/4] spirv: do not emit unused pointer types for variables --- src/codegen/spirv/CodeGen.zig | 37 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index 24d5fac0c6357222e593a996d8cff7152f899e22..a2746513e5a9f4829b7cf6b50d89ed576e73c1dc 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -1684,15 +1684,24 @@ fn constantPtr(cg: *CodeGen, ptr_val: Value) !Id { return cg.derivePtr(ptr_val.typeOf(zcu).ptrAddressSpace(zcu), derivation); } -fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Value.PointerDerivation) !Id { - const gpa = cg.gpa; - const zcu = cg.zcu; - const target = zcu.getTarget(); - - const result_ty_id = try cg.ptrType( +fn derivedPtrType(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Value.PointerDerivation) !Id { + return cg.ptrType( try cg.pointeeType(@"addrspace", derivation.elem_ty, false), cg.storageClass(@"addrspace"), ); +} + +fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Value.PointerDerivation) !Id { + const gpa = cg.gpa; + const zcu = cg.zcu; + const target = zcu.getTarget(); + + // A variable on Vulkan and OpenGL is used through its own pointer, so a `nav` resolves this only + // where it is used; a type emitted and left unused stays in the module. + const result_ty_id = switch (derivation.addr) { + .nav => undefined, + else => try cg.derivedPtrType(@"addrspace", derivation), + }; switch (derivation.addr) { .comptime_alloc, .comptime_field => unreachable, @@ -1727,10 +1736,10 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu if (ip.isFunctionType(nav_ty.toIntern())) { if (is_extern) return try cg.resolveExternFn(nav_index); - return try cg.constUndef(result_ty_id); + return try cg.constUndef(try cg.derivedPtrType(@"addrspace", derivation)); } if (!nav_ty.hasRuntimeBits(zcu) and nav_ty.zigTypeTag(zcu) != .spirv) { - return cg.constUndef(result_ty_id); + return cg.constUndef(try cg.derivedPtrType(@"addrspace", derivation)); } if (!is_extern) { return cg.todo("pointer to constant '{f}'", .{nav.fqn.fmt(ip)}); @@ -1739,23 +1748,23 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu const as = nav.resolved.?.@"addrspace"; assert(as != .generic); - const storage_class = cg.storageClass(as); const var_id = try cg.resolveNav(nav_index); - const nav_ty_id = try cg.resolveType(nav_ty, .indirect); - const decl_ptr_ty_id = try cg.ptrType(nav_ty_id, storage_class); if (cg.needsLayout(as, nav_ty)) { try cg.block_var_ids.put(gpa, var_id, {}); } - - if (decl_ptr_ty_id == result_ty_id) return var_id; switch (target.os.tag) { .vulkan, .opengl => return var_id, else => {}, } + const nav_ty_id = try cg.resolveType(nav_ty, .indirect); + const decl_ptr_ty_id = try cg.ptrType(nav_ty_id, cg.storageClass(as)); + const nav_result_ty_id = try cg.derivedPtrType(@"addrspace", derivation); + if (decl_ptr_ty_id == nav_result_ty_id) return var_id; + const casted_ptr_id = cg.allocId(); try cg.body.emit(gpa, .OpBitcast, .{ - .id_result_type = result_ty_id, + .id_result_type = nav_result_ty_id, .id_result = casted_ptr_id, .operand = var_id, }); -- 2.54.0 From dc71137626b001b7ceff685a9fd533b960f62756 Mon Sep 17 00:00:00 2001 From: Brian Ferri Date: Thu, 1 Oct 2026 22:41:09 +0200 Subject: [PATCH 2/4] spirv: type comptime element pointers by their element --- src/codegen/spirv/CodeGen.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index a2746513e5a9f4829b7cf6b50d89ed576e73c1dc..e059594cbebabe97e830051df2b300dd8747d8c5 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -1810,7 +1810,7 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu .many => .many_ptr, }, @"addrspace", - derived.parent.elem_ty, + derivation.elem_ty, try cg.derivePtr(@"addrspace", derived.parent.*), try cg.constInt(.usize, derived.elem_index), ), -- 2.54.0 From 82281d53fb9cf2883fa099cff1e06790f8099c05 Mon Sep 17 00:00:00 2001 From: Brian Ferri Date: Fri, 2 Oct 2026 08:57:33 +0200 Subject: [PATCH 3/4] Legalize: gate the one-element splat bitcast on its feature --- src/Air/Legalize.zig | 2 +- test/behavior/splat.zig | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Air/Legalize.zig b/src/Air/Legalize.zig index 99dc64577b09583bcdf4f8a51b8efe9ff42c749c..5716615897a7fd445b36ba8d86fe502445a2c346 100644 --- a/src/Air/Legalize.zig +++ b/src/Air/Legalize.zig @@ -904,7 +904,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { .splat => { const ty_op = l.air_instructions.items(.data)[@backingInt(inst)].ty_op; switch (ty_op.ty.zigTypeTag(zcu)) { - .vector => switch (ty_op.ty.vectorLen(zcu)) { + .vector => if (l.features.has(.splat_one_elem_to_bit_cast)) switch (ty_op.ty.vectorLen(zcu)) { 0 => unreachable, 1 => continue :inst l.replaceInst(inst, .bit_cast, .{ .ty_op = .{ .ty = ty_op.ty, diff --git a/test/behavior/splat.zig b/test/behavior/splat.zig index 859dfd8634e8ce9cd6d425194d182740b44d87ac..b440fcbd0aeae203291fc940d4916d1214cb048c 100644 --- a/test/behavior/splat.zig +++ b/test/behavior/splat.zig @@ -145,6 +145,16 @@ test "vector @splat" { try testForT(8 * 2, @as(f16, 3.1415)); try testForT(4 * 2, @as(f32, 3.1415)); try testForT(2 * 2, @as(f64, 3.1415)); + + // Splats into one-element vectors. + try testForT(1, @as(u8, 0xEE)); + try testForT(1, @as(u16, 0xBEEF)); + try testForT(1, @as(u32, 0xDEADBEEF)); + try testForT(1, @as(u64, 0xCAFEF00DDEADBEEF)); + + try testForT(1, @as(f16, 3.1415)); + try testForT(1, @as(f32, 3.1415)); + try testForT(1, @as(f64, 3.1415)); } }; try S.doTheTest(); -- 2.54.0 From 89838eb5f005f206f22116768d6ed3d13507dbd8 Mon Sep 17 00:00:00 2001 From: Brian Ferri Date: Sat, 3 Oct 2026 00:10:06 +0200 Subject: [PATCH 4/4] spirv: build derived pointer types only where they are used --- src/codegen/spirv/CodeGen.zig | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index e059594cbebabe97e830051df2b300dd8747d8c5..b1680336219b8ee00b49472964d45730ee6f792a 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -1684,6 +1684,13 @@ fn constantPtr(cg: *CodeGen, ptr_val: Value) !Id { return cg.derivePtr(ptr_val.typeOf(zcu).ptrAddressSpace(zcu), derivation); } +/// Emits the pointer type that one step of a pointer derivation results in, along with its +/// pointee type. A derivation is the sequence of steps that builds a comptime-known pointer: a +/// base address, such as a declaration, an anonymous value or an integer, followed by field, +/// element and cast steps, each of which is lowered on its own. +/// This function emits instructions on every call, and they stay in the module whether or not they +/// are read, so it should only be called where the resulting type is used: a buffer struct emitted +/// without `Block` fails validation even when nothing refers to it. fn derivedPtrType(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Value.PointerDerivation) !Id { return cg.ptrType( try cg.pointeeType(@"addrspace", derivation.elem_ty, false), @@ -1696,13 +1703,6 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu const zcu = cg.zcu; const target = zcu.getTarget(); - // A variable on Vulkan and OpenGL is used through its own pointer, so a `nav` resolves this only - // where it is used; a type emitted and left unused stays in the module. - const result_ty_id = switch (derivation.addr) { - .nav => undefined, - else => try cg.derivedPtrType(@"addrspace", derivation), - }; - switch (derivation.addr) { .comptime_alloc, .comptime_field => unreachable, .int => |int| { @@ -1717,6 +1717,7 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu // TODO: This can probably be an OpSpecConstantOp Bitcast, but // that is not implemented by Mesa yet. Therefore, just generate it // as a runtime operation. + const result_ty_id = try cg.derivedPtrType(@"addrspace", derivation); const result_ptr_id = cg.allocId(); const value_id = try cg.constInt(.usize, int); try cg.body.emit(gpa, .OpConvertUToPtr, .{ @@ -1759,12 +1760,12 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu const nav_ty_id = try cg.resolveType(nav_ty, .indirect); const decl_ptr_ty_id = try cg.ptrType(nav_ty_id, cg.storageClass(as)); - const nav_result_ty_id = try cg.derivedPtrType(@"addrspace", derivation); - if (decl_ptr_ty_id == nav_result_ty_id) return var_id; + const result_ty_id = try cg.derivedPtrType(@"addrspace", derivation); + if (decl_ptr_ty_id == result_ty_id) return var_id; const casted_ptr_id = cg.allocId(); try cg.body.emit(gpa, .OpBitcast, .{ - .id_result_type = nav_result_ty_id, + .id_result_type = result_ty_id, .id_result = casted_ptr_id, .operand = var_id, }); @@ -1781,7 +1782,7 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu } if (!uav_ty.hasRuntimeBits(zcu) and uav_ty.zigTypeTag(zcu) != .spirv) { - return cg.constUndef(result_ty_id); + return cg.constUndef(try cg.derivedPtrType(@"addrspace", derivation)); } if (cg.storageClass(@"addrspace") != .function) { @@ -1798,7 +1799,7 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu .opt_payload => @panic("TODO"), .field => |derived| return cg.structFieldPtr( @"addrspace", - result_ty_id, + try cg.derivedPtrType(@"addrspace", derivation), derivation.elem_ty, derived.parent.elem_ty, try cg.derivePtr(@"addrspace", derived.parent.*), @@ -1850,12 +1851,13 @@ fn derivePtr(cg: *CodeGen, @"addrspace": std.lang.AddressSpace, derivation: Valu const zero = try cg.constInt(.u32, 0); const ids = try cg.id_scratch.addManyAsSlice(gpa, depth); @memset(ids, zero); - return cg.accessChainId(result_ty_id, parent_ptr_id, ids); + return cg.accessChainId(try cg.derivedPtrType(@"addrspace", derivation), parent_ptr_id, ids); } else { return parent_ptr_id; } } if (target.os.tag == .opencl) { + const result_ty_id = try cg.derivedPtrType(@"addrspace", derivation); const result_ptr_id = cg.allocId(); try cg.body.emit(gpa, .OpBitcast, .{ .id_result_type = result_ty_id, -- 2.54.0