From 508cbec69455d82c0e9bb5ae47f064ab33460469 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Wed, 17 Jun 2026 00:31:17 +0330 Subject: [PATCH] spirv: implement tuple types --- src/codegen/spirv/CodeGen.zig | 18 ++++++++++- src/link/SpirV/lower_invocation_globals.zig | 33 ++++++++++++++------- test/behavior/basic.zig | 6 ---- test/behavior/cast.zig | 2 +- test/behavior/eval.zig | 3 -- test/behavior/math.zig | 3 -- test/behavior/struct.zig | 3 -- test/behavior/tuple.zig | 1 - 8 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index a2c00142b81f69e01df53d1fc24737e732a735d4..afc51f1711d8ff861bea813f863f4b80544bef92 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -1286,7 +1286,23 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { const comp_ty_id = try cg.resolveType(ty, .direct); return try cg.constructComposite(comp_ty_id, constituents.items); }, - .tuple_type => return cg.todo("implement tuple types", .{}), + .tuple_type => |tuple| { + var constituents: std.ArrayList(Id) = .empty; + defer constituents.deinit(gpa); + + for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| { + if (field_val != .none) continue; + const ft: Type = .fromInterned(field_ty); + if (!ft.hasRuntimeBits(zcu)) continue; + + const fv = try val.fieldValue(pt, i); + const field_id = try cg.constant(ft, fv, .indirect); + try constituents.append(gpa, field_id); + } + + const comp_ty_id = try cg.resolveType(ty, .direct); + return try cg.constructComposite(comp_ty_id, constituents.items); + }, else => unreachable, }, .un => |un| { diff --git a/src/link/SpirV/lower_invocation_globals.zig b/src/link/SpirV/lower_invocation_globals.zig index 8588a6143056a04695eff960db31a5cd5deb6df1..7cc9604d049908c09fa63599ac2d0b61359331ef 100644 --- a/src/link/SpirV/lower_invocation_globals.zig +++ b/src/link/SpirV/lower_invocation_globals.zig @@ -457,21 +457,31 @@ const ModuleBuilder = struct { }, .OpEntryPoint => { const original_id: ResultId = @enumFromInt(inst.operands[1]); - const new_id_index = info.entry_points.getIndex(original_id).?; - const new_id: ResultId = @enumFromInt(self.entry_point_new_id_base + new_id_index); - try self.section.emitRaw(self.arena, .OpEntryPoint, inst.operands.len); - self.section.writeWord(inst.operands[0]); - self.section.writeOperand(ResultId, new_id); - self.section.writeWords(inst.operands[2..]); + const fn_info = info.functions.get(original_id).?; + if (fn_info.invocation_globals.count() > 0) { + const new_id_index = info.entry_points.getIndex(original_id).?; + const new_id: ResultId = @enumFromInt(self.entry_point_new_id_base + new_id_index); + try self.section.emitRaw(self.arena, .OpEntryPoint, inst.operands.len); + self.section.writeWord(inst.operands[0]); + self.section.writeOperand(ResultId, new_id); + self.section.writeWords(inst.operands[2..]); + } else { + try self.section.emitRawInstruction(self.arena, inst.opcode, inst.operands); + } continue; }, .OpExecutionMode, .OpExecutionModeId => { const original_id: ResultId = @enumFromInt(inst.operands[0]); - const new_id_index = info.entry_points.getIndex(original_id).?; - const new_id: ResultId = @enumFromInt(self.entry_point_new_id_base + new_id_index); - try self.section.emitRaw(self.arena, inst.opcode, inst.operands.len); - self.section.writeOperand(ResultId, new_id); - self.section.writeWords(inst.operands[1..]); + const fn_info = info.functions.get(original_id).?; + if (fn_info.invocation_globals.count() > 0) { + const new_id_index = info.entry_points.getIndex(original_id).?; + const new_id: ResultId = @enumFromInt(self.entry_point_new_id_base + new_id_index); + try self.section.emitRaw(self.arena, inst.opcode, inst.operands.len); + self.section.writeOperand(ResultId, new_id); + self.section.writeWords(inst.operands[1..]); + } else { + try self.section.emitRawInstruction(self.arena, inst.opcode, inst.operands); + } continue; }, .OpTypeFunction => { @@ -652,6 +662,7 @@ const ModuleBuilder = struct { for (info.entry_points.keys(), 0..) |func, entry_point_index| { const fn_info = info.functions.get(func).?; + if (fn_info.invocation_globals.count() == 0) continue; const ep_id: ResultId = @enumFromInt(self.entry_point_new_id_base + @as(u32, @intCast(entry_point_index))); const fn_type = self.function_types.get(.{ .return_type = fn_info.return_type, diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 3b33c1e334384bc99169dd062ff305b6c2907487..f1d3ddd762dfd16cb81e7d73d3863d050adf249d 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -302,9 +302,6 @@ test "compile time global reinterpret" { } test "cast undefined" { - // if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - const array: [100]u8 = undefined; const slice = @as([]const u8, &array); testCastUndefined(slice); @@ -374,9 +371,7 @@ fn fB() []const u8 { test "call function pointer in struct" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - try expect(mem.eql(u8, f3(true), "a")); - try expect(mem.eql(u8, f3(false), "b")); } fn f3(x: bool) []const u8 { @@ -417,7 +412,6 @@ test "call result of if else expression" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try expect(mem.eql(u8, f2(true), "a")); - try expect(mem.eql(u8, f2(false), "b")); } fn f2(x: bool) []const u8 { return (if (x) &fA else &fB)(); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 661dd1ce1bd030196b712038fdb937751cf0bc0b..418975a8b7242c8d07822fa2599f2e3e858ddef6 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -2541,7 +2541,7 @@ test "peer type resolution: many compatible pointers" { test "peer type resolution: tuples with comptime fields" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO + // if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO const a = .{ 1, 2 }; const b = .{ @as(u32, 3), @as(i16, 4) }; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 762ec867e560437c640af40485436559344f69e2..53eaf1afab84e39a0ab1abf1bf022b77d511bc9d 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1159,8 +1159,6 @@ test "repeated value is correctly expanded" { } test "value in if block is comptime-known" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const first = blk: { const s = if (false) "a" else "b"; break :blk "foo" ++ s; @@ -1196,7 +1194,6 @@ test "lazy value is resolved as slice operand" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const A = struct { a: u32 }; var a: [512]u64 = undefined; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 36ab4182cd72b0dcc75515f931386fe4a402f79c..cf6b6f60b50e643b8131123accc4e7b62f3b1c77 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -456,8 +456,6 @@ test "division" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - try testIntDivision(); try comptime testIntDivision(); @@ -576,7 +574,6 @@ test "large integer division" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; { var numerator: u256 = 99999999999999999997315645440; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index fce9cca5675a2b42b4b85ce220f7cc9ee6df8bf2..af066defb934bd33808a85be2f88825e22f1ad4c 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -503,8 +503,6 @@ test "implicit cast packed struct field to const ptr" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO const LevelUpMove = packed struct { move_id: u9, @@ -538,7 +536,6 @@ test "packed struct with non-ABI-aligned field" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = packed struct { x: u9, diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 8218ae55a2a392cb6841dab9aa0b31920ddabbc5..dabc3eb01af28b08e8cdcae8de076069ff054693 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -225,7 +225,6 @@ test "initializing anon struct with mixed comptime-runtime fields" { test "tuple in tuple passed to generic function" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { fn pair(x: f32, y: f32) @Tuple(&.{ f32, f32 }) { return .{ x, y }; -- 2.54.0