authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-07-08 16:44:29+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-07-10 11:39:54+03:30
logc498b66434412616a97328a7f7a05014aa5183dc
tree5bd059bc5bb8bf9c3629e7706d56343571418011
parent332c73ccd2246af417afaa42597055c9ba6255c1

spirv: spec constants

add `std.spirv.specConst()` and support lowering literal values as string in inline assembly

4 files changed, 262 insertions(+), 48 deletions(-)

lib/std/spirv.zig+30
...@@ -82,3 +82,33 @@ pub fn workgroupBarrier() void {...@@ -82,3 +82,33 @@ pub fn workgroupBarrier() void {
82 .{ .acquire_release = true, .workgroup_memory = true },82 .{ .acquire_release = true, .workgroup_memory = true },
83 );83 );
84}84}
85
86pub fn specConst(T: type, comptime default_value: T, comptime spec_id: u32) T {
87 switch (@typeInfo(T)) {
88 .bool => {
89 const op = if (default_value) "OpSpecConstantTrue" else "OpSpecConstantFalse";
90 return asm ("%ret = " ++ op ++ " %ty\n" ++
91 "OpDecorate %ret SpecId $spec_id"
92 : [ret] "" (-> T),
93 : [ty] "t" (T),
94 [spec_id] "c" (spec_id),
95 );
96 },
97 .int, .float => return asm (
98 \\%ret = OpSpecConstant %ty $default_value
99 \\OpDecorate %ret SpecId $spec_id"
100 : [ret] "" (-> T),
101 : [ty] "t" (T),
102 [default_value] "c" (default_value),
103 [spec_id] "c" (spec_id),
104 ),
105 .vector => return asm (
106 \\%ret = OpSpecConstantComposite %ty %default_value %spec_id
107 : [ret] "" (-> T),
108 : [ty] "t" (T),
109 [default_value] "c" (default_value),
110 [spec_id] "c" (spec_id),
111 ),
112 else => @compileError("unsupported spec constant type"),
113 }
114}
src/codegen/spirv/Assembler.zig+153-7
...@@ -58,6 +58,10 @@ const Operand = union(enum) {...@@ -58,6 +58,10 @@ const Operand = union(enum) {
58pub fn deinit(ass: *Assembler) void {58pub fn deinit(ass: *Assembler) void {
59 const gpa = ass.cg.gpa;59 const gpa = ass.cg.gpa;
60 for (ass.errors.items) |err| gpa.free(err.msg);60 for (ass.errors.items) |err| gpa.free(err.msg);
61 for (ass.value_map.values()) |v| switch (v) {
62 .constant_composite => |cc| gpa.free(cc.values),
63 else => {},
64 };
61 ass.tokens.deinit(gpa);65 ass.tokens.deinit(gpa);
62 ass.errors.deinit(gpa);66 ass.errors.deinit(gpa);
63 ass.inst.operands.deinit(gpa);67 ass.inst.operands.deinit(gpa);
...@@ -132,8 +136,18 @@ const AsmValue = union(enum) {...@@ -132,8 +136,18 @@ const AsmValue = union(enum) {
132 value: Id,136 value: Id,
133 /// A type registered into the module's type system.137 /// A type registered into the module's type system.
134 ty: Id,138 ty: Id,
135 /// A pre-supplied constant integer value.139 /// A pre-supplied constant value, holding the raw bit pattern of the input.
136 constant: u32,140 /// For integers the value is sign-extended (for signed) or zero-extended
141 /// (for unsigned) to 64 bits. For floats, the value is the bit pattern
142 /// zero-extended from the float's width to 64 bits.
143 constant: u64,
144 /// A vector "c" input expanded by `processSpecConstVector`.
145 constant_composite: struct {
146 child: Id,
147 child_kind: std.lang.TypeId,
148 child_bit_width: u16,
149 values: []u64,
150 },
137 string: []const u8,151 string: []const u8,
138152
139 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue153 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue
...@@ -145,6 +159,7 @@ const AsmValue = union(enum) {...@@ -145,6 +159,7 @@ const AsmValue = union(enum) {
145 .unresolved_forward_reference,159 .unresolved_forward_reference,
146 // TODO: Lower this value as constant?160 // TODO: Lower this value as constant?
147 .constant,161 .constant,
162 .constant_composite,
148 .string,163 .string,
149 => unreachable,164 => unreachable,
150 .value => |result| result,165 .value => |result| result,
...@@ -178,6 +193,12 @@ fn processInstruction(ass: *Assembler) !void {...@@ -178,6 +193,12 @@ fn processInstruction(ass: *Assembler) !void {
178 };193 };
179 break :blk .{ .value = try cg.importInstructionSet(set_tag) };194 break :blk .{ .value = try cg.importInstructionSet(set_tag) };
180 },195 },
196 .OpSpecConstantComposite => blk: {
197 if (try ass.processSpecConstVector()) |result| {
198 break :blk result;
199 }
200 break :blk (try ass.processGenericInstruction()) orelse return;
201 },
181 else => switch (ass.inst.opcode.class()) {202 else => switch (ass.inst.opcode.class()) {
182 .type_declaration => try ass.processTypeInstruction(),203 .type_declaration => try ass.processTypeInstruction(),
183 else => (try ass.processGenericInstruction()) orelse return,204 else => (try ass.processGenericInstruction()) orelse return,
...@@ -398,6 +419,87 @@ fn processGenericInstruction(ass: *Assembler) !?AsmValue {...@@ -398,6 +419,87 @@ fn processGenericInstruction(ass: *Assembler) !?AsmValue {
398 return null;419 return null;
399}420}
400421
422/// Handles `%ret = OpSpecConstantComposite %ty %vec %spec_id` where `%vec` is a
423/// vector `"c"` input and `%spec_id` is a base SpecId `"c"` input.
424/// returns null to fall back to normal processing.
425fn processSpecConstVector(ass: *Assembler) !?AsmValue {
426 if (ass.inst.operands.items.len != 4) return null;
427 const vec_ref = switch (ass.inst.operands.items[2]) {
428 .ref_id => |i| i,
429 else => return null,
430 };
431 const sid_ref = switch (ass.inst.operands.items[3]) {
432 .ref_id => |i| i,
433 else => return null,
434 };
435 const cc = switch (try ass.resolveRef(vec_ref)) {
436 .constant_composite => |cc| cc,
437 else => return null,
438 };
439 const spec_id_base = switch (try ass.resolveRef(sid_ref)) {
440 .constant => |v| v,
441 else => return null,
442 };
443
444 const cg = ass.cg;
445 const gpa = cg.gpa;
446 const ty_ref = switch (ass.inst.operands.items[0]) {
447 .ref_id => |i| i,
448 else => return ass.fail(0, "missing result type", .{}),
449 };
450 const composite_ty_id = switch (try ass.resolveRef(ty_ref)) {
451 .ty => |id| id,
452 else => return ass.fail(0, "%ty must be a type", .{}),
453 };
454
455 const globals = &cg.sections.globals;
456 const annotations = &cg.sections.annotations;
457 const literal_words: usize = if (cc.child_bit_width <= @bitSizeOf(Word)) 1 else 2;
458
459 const elem_ids = try gpa.alloc(Id, cc.values.len);
460 defer gpa.free(elem_ids);
461 for (cc.values, elem_ids, 0..) |value, *elem_id_out, i| {
462 const elem_id = cg.allocId();
463 elem_id_out.* = elem_id;
464
465 switch (cc.child_kind) {
466 .bool => {
467 const opcode: Opcode = if (value & 1 != 0) .OpSpecConstantTrue else .OpSpecConstantFalse;
468 try globals.emitRaw(gpa, opcode, 2);
469 globals.writeOperand(Id, cc.child);
470 globals.writeOperand(Id, elem_id);
471 },
472 .int, .float => {
473 try globals.emitRaw(gpa, .OpSpecConstant, 2 + literal_words);
474 globals.writeOperand(Id, cc.child);
475 globals.writeOperand(Id, elem_id);
476 if (literal_words == 1) {
477 globals.writeWord(@truncate(value));
478 } else {
479 globals.writeDoubleWord(value);
480 }
481 },
482 else => unreachable,
483 }
484
485 const spec_id_word = std.math.cast(u32, spec_id_base + i) orelse {
486 return ass.fail(0, "SpecId {} does not fit in 32 bits", .{spec_id_base + i});
487 };
488 try annotations.emitRaw(gpa, .OpDecorate, 3);
489 annotations.writeOperand(Id, elem_id);
490 annotations.writeWord(@intFromEnum(spec.Decoration.spec_id));
491 annotations.writeWord(spec_id_word);
492 }
493
494 const result_id = cg.allocId();
495 try globals.emitRaw(gpa, .OpSpecConstantComposite, 2 + cc.values.len);
496 globals.writeOperand(Id, composite_ty_id);
497 globals.writeOperand(Id, result_id);
498 for (elem_ids) |id| globals.writeOperand(Id, id);
499
500 return .{ .value = result_id };
501}
502
401fn resolveMaybeForwardRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue {503fn resolveMaybeForwardRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue {
402 const value = ass.value_map.values()[ref];504 const value = ass.value_map.values()[ref];
403 switch (value) {505 switch (value) {
...@@ -579,7 +681,14 @@ fn parseValueEnum(ass: *Assembler, kind: spec.OperandKind) !void {...@@ -579,7 +681,14 @@ fn parseValueEnum(ass: *Assembler, kind: spec.OperandKind) !void {
579 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});681 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
580 };682 };
581 switch (value) {683 switch (value) {
582 .constant => |literal32| {684 .constant => |literal| {
685 const literal32 = std.math.cast(u32, literal) orelse {
686 return ass.fail(
687 tok.start,
688 "placeholder value {} does not fit in 32 bits",
689 .{literal},
690 );
691 };
583 try ass.inst.operands.append(gpa, .{ .value = literal32 });692 try ass.inst.operands.append(gpa, .{ .value = literal32 });
584 },693 },
585 .string => |str| {694 .string => |str| {
...@@ -646,7 +755,14 @@ fn parseLiteralInteger(ass: *Assembler) !void {...@@ -646,7 +755,14 @@ fn parseLiteralInteger(ass: *Assembler) !void {
646 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});755 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
647 };756 };
648 switch (value) {757 switch (value) {
649 .constant => |literal32| {758 .constant => |literal| {
759 const literal32 = std.math.cast(u32, literal) orelse {
760 return ass.fail(
761 tok.start,
762 "placeholder value {} does not fit in 32 bits",
763 .{literal},
764 );
765 };
650 try ass.inst.operands.append(gpa, .{ .literal32 = literal32 });766 try ass.inst.operands.append(gpa, .{ .literal32 = literal32 });
651 },767 },
652 else => {768 else => {
...@@ -679,7 +795,14 @@ fn parseLiteralExtInstInteger(ass: *Assembler) !void {...@@ -679,7 +795,14 @@ fn parseLiteralExtInstInteger(ass: *Assembler) !void {
679 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});795 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
680 };796 };
681 switch (value) {797 switch (value) {
682 .constant => |literal32| {798 .constant => |literal| {
799 const literal32 = std.math.cast(u32, literal) orelse {
800 return ass.fail(
801 tok.start,
802 "placeholder value {} does not fit in 32 bits",
803 .{literal},
804 );
805 };
683 try ass.inst.operands.append(gpa, .{ .literal32 = literal32 });806 try ass.inst.operands.append(gpa, .{ .literal32 = literal32 });
684 },807 },
685 else => {808 else => {
...@@ -767,8 +890,12 @@ fn parseContextDependentInt(ass: *Assembler, signedness: std.lang.Signedness, wi...@@ -767,8 +890,12 @@ fn parseContextDependentInt(ass: *Assembler, signedness: std.lang.Signedness, wi
767 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});890 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
768 };891 };
769 switch (value) {892 switch (value) {
770 .constant => |literal32| {893 .constant => |literal| {
771 try ass.inst.operands.append(gpa, .{ .literal32 = literal32 });894 if (width <= @bitSizeOf(spec.Word)) {
895 try ass.inst.operands.append(gpa, .{ .literal32 = @truncate(literal) });
896 } else {
897 try ass.inst.operands.append(gpa, .{ .literal64 = literal });
898 }
772 },899 },
773 else => {900 else => {
774 return ass.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name});901 return ass.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name});
...@@ -815,6 +942,25 @@ fn parseContextDependentFloat(ass: *Assembler, comptime width: u16) !void {...@@ -815,6 +942,25 @@ fn parseContextDependentFloat(ass: *Assembler, comptime width: u16) !void {
815 const Int = @Int(.unsigned, width);942 const Int = @Int(.unsigned, width);
816943
817 const tok = ass.currentToken();944 const tok = ass.currentToken();
945 if (ass.eatToken(.placeholder)) {
946 const name = ass.tokenText(tok)[1..];
947 const value = ass.value_map.get(name) orelse {
948 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
949 };
950 switch (value) {
951 .constant => |literal| {
952 if (width <= @bitSizeOf(spec.Word)) {
953 try ass.inst.operands.append(gpa, .{ .literal32 = @truncate(literal) });
954 } else {
955 try ass.inst.operands.append(gpa, .{ .literal64 = literal });
956 }
957 },
958 else => {
959 return ass.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name});
960 },
961 }
962 return;
963 }
818 try ass.expectToken(.value);964 try ass.expectToken(.value);
819965
820 const text = ass.tokenText(tok);966 const text = ass.tokenText(tok);
src/codegen/spirv/CodeGen.zig+78-40
...@@ -423,7 +423,7 @@ pub fn addEntryPointDeps(...@@ -423,7 +423,7 @@ pub fn addEntryPointDeps(
423 cg: *CodeGen,423 cg: *CodeGen,
424 decl_index: Decl.Index,424 decl_index: Decl.Index,
425 seen: *std.bit_set.Dynamic,425 seen: *std.bit_set.Dynamic,
426 interface: *std.array_list.Managed(Id),426 interface: *std.ArrayList(Id),
427) !void {427) !void {
428 const decl = cg.declPtr(decl_index);428 const decl = cg.declPtr(decl_index);
429 const deps = cg.decl_deps.items[decl.begin_dep..decl.end_dep];429 const deps = cg.decl_deps.items[decl.begin_dep..decl.end_dep];
...@@ -435,7 +435,7 @@ pub fn addEntryPointDeps(...@@ -435,7 +435,7 @@ pub fn addEntryPointDeps(
435 seen.set(@intFromEnum(decl_index));435 seen.set(@intFromEnum(decl_index));
436436
437 if (decl.kind == .global) {437 if (decl.kind == .global) {
438 try interface.append(decl.result_id);438 try interface.append(cg.gpa, decl.result_id);
439 }439 }
440440
441 for (deps) |dep| {441 for (deps) |dep| {
...@@ -1806,11 +1806,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {...@@ -1806,11 +1806,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
1806 const struct_type = zcu.typeToStruct(ty).?;1806 const struct_type = zcu.typeToStruct(ty).?;
1807 assert(struct_type.layout != .@"packed"); // packed structs use `bitpack`1807 assert(struct_type.layout != .@"packed"); // packed structs use `bitpack`
18081808
1809 var types = std.array_list.Managed(Type).init(gpa);1809 var types: std.ArrayList(Type) = .empty;
1810 defer types.deinit();1810 defer types.deinit(gpa);
18111811
1812 var constituents = std.array_list.Managed(Id).init(gpa);1812 var constituents: std.ArrayList(Id) = .empty;
1813 defer constituents.deinit();1813 defer constituents.deinit(gpa);
18141814
1815 var it = struct_type.iterateRuntimeOrder(ip);1815 var it = struct_type.iterateRuntimeOrder(ip);
1816 while (it.next()) |field_index| {1816 while (it.next()) |field_index| {
...@@ -1824,8 +1824,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {...@@ -1824,8 +1824,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
1824 const field_val = try val.fieldValue(pt, field_index);1824 const field_val = try val.fieldValue(pt, field_index);
1825 const field_id = try cg.constant(field_ty, field_val, .indirect);1825 const field_id = try cg.constant(field_ty, field_val, .indirect);
18261826
1827 try types.append(field_ty);1827 try types.append(gpa, field_ty);
1828 try constituents.append(field_id);1828 try constituents.append(gpa, field_id);
1829 }1829 }
18301830
1831 const comp_ty_id = try cg.resolveType(ty, .direct);1831 const comp_ty_id = try cg.resolveType(ty, .direct);
...@@ -2366,11 +2366,11 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -2366,11 +2366,11 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2366 return try cg.resolveType(.fromInterned(struct_type.packed_backing_int_type), .direct);2366 return try cg.resolveType(.fromInterned(struct_type.packed_backing_int_type), .direct);
2367 }2367 }
23682368
2369 var member_types = std.array_list.Managed(Id).init(gpa);2369 var member_types: std.ArrayList(Id) = .empty;
2370 defer member_types.deinit();2370 defer member_types.deinit(gpa);
23712371
2372 var member_names = std.array_list.Managed([]const u8).init(gpa);2372 var member_names: std.ArrayList([]const u8) = .empty;
2373 defer member_names.deinit();2373 defer member_names.deinit(gpa);
23742374
2375 var it = struct_type.iterateRuntimeOrder(ip);2375 var it = struct_type.iterateRuntimeOrder(ip);
2376 while (it.next()) |field_index| {2376 while (it.next()) |field_index| {
...@@ -2378,8 +2378,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -2378,8 +2378,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2378 if (!field_ty.hasRuntimeBits(zcu)) continue;2378 if (!field_ty.hasRuntimeBits(zcu)) continue;
23792379
2380 const field_name = struct_type.field_names.get(ip)[field_index];2380 const field_name = struct_type.field_names.get(ip)[field_index];
2381 try member_types.append(try cg.resolveType(field_ty, .indirect));2381 try member_types.append(gpa, try cg.resolveType(field_ty, .indirect));
2382 try member_names.append(field_name.toSlice(ip));2382 try member_names.append(gpa, field_name.toSlice(ip));
2383 }2383 }
23842384
2385 const result_id = try cg.structType(2385 const result_id = try cg.structType(
...@@ -8688,31 +8688,69 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -8688,31 +8688,69 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
8688 });8688 });
86898689
8690 const ip = &zcu.intern_pool;8690 const ip = &zcu.intern_pool;
8691 switch (ip.indexToKey(val.toIntern())) {8691 const target = cg.pt.zcu.getTarget();
8692 .int_type,8692 if (ip.indexToKey(val.toIntern()) == .undef) {
8693 .ptr_type,8693 return cg.fail("assembly input with 'c' constraint cannot be undefined", .{});
8694 .array_type,8694 }
8695 .vector_type,8695 switch (input_ty.zigTypeTag(zcu)) {
8696 .opt_type,8696 .int => {
8697 .anyframe_type,8697 const bits: u64 = switch (input_ty.intInfo(zcu).signedness) {
8698 .error_union_type,8698 .unsigned => val.toUnsignedInt(zcu),
8699 .simple_type,8699 .signed => @bitCast(val.toSignedInt(zcu)),
8700 .struct_type,8700 };
8701 .union_type,8701 try ass.value_map.put(gpa, in.name, .{ .constant = bits });
8702 .opaque_type,8702 },
8703 .spirv_type,8703 .float => {
8704 .enum_type,8704 const bits: u64 = switch (input_ty.floatBits(target)) {
8705 .func_type,8705 16 => @as(u16, @bitCast(val.toFloat(f16, zcu))),
8706 .error_set_type,8706 32 => @as(u32, @bitCast(val.toFloat(f32, zcu))),
8707 .inferred_error_set_type,8707 64 => @bitCast(val.toFloat(f64, zcu)),
8708 => unreachable, // types, not values8708 else => return cg.fail("unsupported float width for 'c' constraint", .{}),
87098709 };
8710 .undef => return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}),8710 try ass.value_map.put(gpa, in.name, .{ .constant = bits });
87118711 },
8712 .int => try ass.value_map.put(gpa, in.name, .{ .constant = @intCast(val.toUnsignedInt(zcu)) }),8712 .vector => {
8713 .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }),8713 const child_ty = input_ty.childType(zcu);
87148714 const child_kind = child_ty.zigTypeTag(zcu);
8715 else => unreachable, // TODO8715 const child_bit_width: u16 = switch (child_kind) {
8716 .bool => 0,
8717 .int => @intCast(child_ty.intInfo(zcu).bits),
8718 .float => child_ty.floatBits(target),
8719 else => return cg.fail("'c' constraint vector element must be bool, int, or float", .{}),
8720 };
8721 const vec_len: usize = @intCast(input_ty.vectorLen(zcu));
8722 const values = try gpa.alloc(u64, vec_len);
8723 errdefer gpa.free(values);
8724 for (values, 0..) |*out, i| {
8725 const elem: Value = try val.elemValue(cg.pt, i);
8726 out.* = switch (child_kind) {
8727 .bool => @intFromBool(elem.toBool()),
8728 .int => switch (child_ty.intInfo(zcu).signedness) {
8729 .unsigned => elem.toUnsignedInt(zcu),
8730 .signed => @bitCast(elem.toSignedInt(zcu)),
8731 },
8732 .float => switch (child_bit_width) {
8733 16 => @as(u16, @bitCast(elem.toFloat(f16, zcu))),
8734 32 => @as(u32, @bitCast(elem.toFloat(f32, zcu))),
8735 64 => @bitCast(elem.toFloat(f64, zcu)),
8736 else => unreachable,
8737 },
8738 else => unreachable,
8739 };
8740 }
8741 const child_ty_id = try cg.resolveType(child_ty, .direct);
8742 try ass.value_map.put(gpa, in.name, .{ .constant_composite = .{
8743 .child = child_ty_id,
8744 .child_kind = child_kind,
8745 .child_bit_width = child_bit_width,
8746 .values = values,
8747 } });
8748 },
8749 .@"enum" => switch (ip.indexToKey(val.toIntern())) {
8750 .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }),
8751 else => unreachable,
8752 },
8753 else => return cg.fail("unsupported type for 'c' constraint", .{}),
8716 }8754 }
8717 } else if (std.mem.eql(u8, in.constraint, "t")) {8755 } else if (std.mem.eql(u8, in.constraint, "t")) {
8718 // type8756 // type
...@@ -8779,7 +8817,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -8779,7 +8817,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
8779 .just_declared, .unresolved_forward_reference => unreachable,8817 .just_declared, .unresolved_forward_reference => unreachable,
8780 .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}),8818 .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}),
8781 .value => |ref| return ref,8819 .value => |ref| return ref,
8782 .constant, .string => return cg.fail("cannot return constant from assembly", .{}),8820 .constant, .constant_composite, .string => return cg.fail("cannot return constant from assembly", .{}),
8783 }8821 }
8784 // TODO: Multiple results8822 // TODO: Multiple results
8785 // TODO: Check that the output type from assembly is the same as the type actually expected by Zig.8823 // TODO: Check that the output type from assembly is the same as the type actually expected by Zig.
src/link/SpirV/BinaryModule.zig+1-1
...@@ -303,7 +303,7 @@ pub const Parser = struct {...@@ -303,7 +303,7 @@ pub const Parser = struct {
303 }303 }
304 },304 },
305 .literal_context_dependent_number => {305 .literal_context_dependent_number => {
306 assert(inst.opcode == .OpConstant or inst.opcode == .OpSpecConstantOp);306 assert(inst.opcode == .OpConstant or inst.opcode == .OpSpecConstant);
307 const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse {307 const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse {
308 log.err("invalid LiteralContextDependentNumber type {}", .{inst.operands[0]});308 log.err("invalid LiteralContextDependentNumber type {}", .{inst.operands[0]});
309 return error.InvalidId;309 return error.InvalidId;