authorgravatar for alichraghi@noreply.codeberg.orgAli Cheraghi <alichraghi@noreply.codeberg.org> 2026-07-12 20:19:37+02:00
committergravatar for alichraghi@noreply.codeberg.orgAli Cheraghi <alichraghi@noreply.codeberg.org> 2026-07-12 20:19:37+02:00
logaddc3c3b8cfb03be7ddee89949eccb22af793887
tree19cb14d17c92a950e5395872d65ecc4268aa5b0c
parent6c7637d85fe09369f3fd557a88dba3933126772e
parent11d8a359d6447d29d8fda37ee99e0e22e67d352d

Merge pull request 'spirv: various enhancements' (#36123) from alichraghi/zig:master into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36123

14 files changed, 690 insertions(+), 242 deletions(-)

lib/std/Target.zig+2-1
......@@ -2334,7 +2334,8 @@ pub fn supportsAddressSpace(
23342334 .constant => (is_gpu and (context == null or context == .constant)) or
23352335 (is_spirv and (context == null or context == .constant or context == .pointer)),
23362336 .param => is_nvptx,
2337 .input, .output, .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => is_spirv,
2337 .input, .output, .uniform, .push_constant, .storage_buffer => is_spirv,
2338 .physical_storage_buffer => arch == .spirv64,
23382339 .externref, .funcref => target.cpu.has(.wasm, .reference_types),
23392340 };
23402341}
lib/std/spirv.zig+30
......@@ -82,3 +82,33 @@ pub fn workgroupBarrier() void {
8282 .{ .acquire_release = true, .workgroup_memory = true },
8383 );
8484}
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/Sema.zig+66-5
......@@ -6917,6 +6917,9 @@ fn analyzeCall(
69176917 const is_inline_call = block.isComptime() or inline_requested;
69186918
69196919 if (!is_inline_call) {
6920 if (func_val == null and !func_is_extern and !block.is_typeof and zcu.getTarget().cpu.arch.isSpirV()) {
6921 return sema.fail(block, func_src, "SPIR-V does not support calling function pointers", .{});
6922 }
69206923 if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: {
69216924 const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)});
69226925 errdefer msg.destroy(gpa);
......@@ -15250,6 +15253,23 @@ fn zirAsm(
1525015253 }
1525115254
1525215255 const constraint = sema.code.nullTerminatedString(input.data.constraint);
15256 if (zcu.getTarget().cpu.arch.isSpirV() and std.mem.eql(u8, constraint, "c")) {
15257 const val = sema.resolveValue(arg.*) orelse {
15258 return sema.fail(block, input_src, "assembly input with 'c' constraint must be compile-time known", .{});
15259 };
15260 if (val.isUndef(zcu)) {
15261 return sema.fail(block, input_src, "assembly input with 'c' constraint cannot be undefined", .{});
15262 }
15263 const bad_type: bool = switch (uncasted_arg_ty.zigTypeTag(zcu)) {
15264 .bool, .int, .float, .comptime_int, .comptime_float, .enum_literal => false,
15265 .vector => switch (uncasted_arg_ty.childType(zcu).zigTypeTag(zcu)) {
15266 .bool, .int, .float => false,
15267 else => true,
15268 },
15269 else => true,
15270 };
15271 if (bad_type) return sema.fail(block, input_src, "unsupported type '{f}' for 'c' constraint", .{uncasted_arg_ty.fmt(pt)});
15272 }
1525315273 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
1525415274 inputs[arg_i] = .{ .c = constraint, .n = name };
1525515275 }
......@@ -21915,6 +21935,34 @@ fn ptrCastFull(
2191521935
2191621936 try sema.validateRuntimeValue(block, operand_src, operand);
2191721937
21938 if (zcu.getTarget().cpu.arch.isSpirV() and
21939 src_info.flags.address_space != .physical_storage_buffer and
21940 src_info.flags.address_space == dest_info.flags.address_space and
21941 src_info.child != dest_info.child and
21942 Type.fromInterned(dest_info.child).hasRuntimeBits(zcu))
21943 {
21944 var cur: Type = .fromInterned(src_info.child);
21945 while (cur.toIntern() != dest_info.child) {
21946 cur = switch (cur.zigTypeTag(zcu)) {
21947 .array, .vector => cur.childType(zcu),
21948 .@"struct" => if (cur.structFieldOffset(0, zcu) == 0) cur.fieldType(0, zcu) else null,
21949 else => null,
21950 } orelse return sema.failWithOwnedErrorMsg(block, msg: {
21951 const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{
21952 operand_ty.fmt(pt), dest_ty.fmt(pt),
21953 });
21954 errdefer msg.destroy(sema.gpa);
21955 try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{
21956 Type.fromInterned(dest_info.child).fmt(pt), Type.fromInterned(src_info.child).fmt(pt),
21957 });
21958 try sema.errNote(src, msg, "'{s}' pointers can only reach nested types through a first struct field or an array element", .{
21959 @tagName(src_info.flags.address_space),
21960 });
21961 break :msg msg;
21962 });
21963 }
21964 }
21965
2191821966 const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));
2191921967 const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);
2192021968 const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align);
......@@ -34488,11 +34536,24 @@ pub fn resolveNavPtrModifiers(
3448834536 },
3448934537 };
3449034538 const target = zcu.getTarget();
34491 const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
34492 .function => target_util.defaultAddressSpace(target, .function),
34493 .variable => target_util.defaultAddressSpace(target, .global_mutable),
34494 .constant => target_util.defaultAddressSpace(target, .global_constant),
34495 else => unreachable,
34539 const addrspace_body = zir_decl.addrspace_body orelse {
34540 if (zir_decl.linkage == .@"extern" and
34541 target.cpu.arch.isSpirV() and
34542 nav_ty.zigTypeTag(zcu) != .@"fn")
34543 {
34544 return sema.fail(
34545 block,
34546 block.src(.{ .node_offset_var_decl_ty = .zero }),
34547 "SPIR-V extern variables require an explicit address space",
34548 .{},
34549 );
34550 }
34551 break :as switch (addrspace_ctx) {
34552 .function => target_util.defaultAddressSpace(target, .function),
34553 .variable => target_util.defaultAddressSpace(target, .global_mutable),
34554 .constant => target_util.defaultAddressSpace(target, .global_constant),
34555 else => unreachable,
34556 };
3449634557 };
3449734558 const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst);
3449834559 break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);
src/codegen/spirv/Assembler.zig+153-7
......@@ -58,6 +58,10 @@ const Operand = union(enum) {
5858pub fn deinit(ass: *Assembler) void {
5959 const gpa = ass.cg.gpa;
6060 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 };
6165 ass.tokens.deinit(gpa);
6266 ass.errors.deinit(gpa);
6367 ass.inst.operands.deinit(gpa);
......@@ -132,8 +136,18 @@ const AsmValue = union(enum) {
132136 value: Id,
133137 /// A type registered into the module's type system.
134138 ty: Id,
135 /// A pre-supplied constant integer value.
136 constant: u32,
139 /// A pre-supplied constant value, holding the raw bit pattern of the input.
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 },
137151 string: []const u8,
138152
139153 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue
......@@ -145,6 +159,7 @@ const AsmValue = union(enum) {
145159 .unresolved_forward_reference,
146160 // TODO: Lower this value as constant?
147161 .constant,
162 .constant_composite,
148163 .string,
149164 => unreachable,
150165 .value => |result| result,
......@@ -178,6 +193,12 @@ fn processInstruction(ass: *Assembler) !void {
178193 };
179194 break :blk .{ .value = try cg.importInstructionSet(set_tag) };
180195 },
196 .OpSpecConstantComposite => blk: {
197 if (try ass.processSpecConstVector()) |result| {
198 break :blk result;
199 }
200 break :blk (try ass.processGenericInstruction()) orelse return;
201 },
181202 else => switch (ass.inst.opcode.class()) {
182203 .type_declaration => try ass.processTypeInstruction(),
183204 else => (try ass.processGenericInstruction()) orelse return,
......@@ -398,6 +419,87 @@ fn processGenericInstruction(ass: *Assembler) !?AsmValue {
398419 return null;
399420}
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
401503fn resolveMaybeForwardRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue {
402504 const value = ass.value_map.values()[ref];
403505 switch (value) {
......@@ -579,7 +681,14 @@ fn parseValueEnum(ass: *Assembler, kind: spec.OperandKind) !void {
579681 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
580682 };
581683 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 };
583692 try ass.inst.operands.append(gpa, .{ .value = literal32 });
584693 },
585694 .string => |str| {
......@@ -646,7 +755,14 @@ fn parseLiteralInteger(ass: *Assembler) !void {
646755 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
647756 };
648757 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 };
650766 try ass.inst.operands.append(gpa, .{ .literal32 = literal32 });
651767 },
652768 else => {
......@@ -679,7 +795,14 @@ fn parseLiteralExtInstInteger(ass: *Assembler) !void {
679795 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
680796 };
681797 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 };
683806 try ass.inst.operands.append(gpa, .{ .literal32 = literal32 });
684807 },
685808 else => {
......@@ -767,8 +890,12 @@ fn parseContextDependentInt(ass: *Assembler, signedness: std.lang.Signedness, wi
767890 return ass.fail(tok.start, "invalid placeholder '${s}'", .{name});
768891 };
769892 switch (value) {
770 .constant => |literal32| {
771 try ass.inst.operands.append(gpa, .{ .literal32 = literal32 });
893 .constant => |literal| {
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 }
772899 },
773900 else => {
774901 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 {
815942 const Int = @Int(.unsigned, width);
816943
817944 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 }
818964 try ass.expectToken(.value);
819965
820966 const text = ass.tokenText(tok);
src/codegen/spirv/CodeGen.zig+328-223
......@@ -37,6 +37,10 @@ prologue: Section = .{},
3737body: Section = .{},
3838args: std.ArrayList(Id) = .empty,
3939next_arg_index: u32 = 0,
40/// Caches the limb extractions for composite integer values so repeated
41/// arithmetic on the same operand doesn't re-emit `OpCompositeExtract` per
42/// limb per use. Slices are owned by `cg.arena`.
43composite_limbs: std.AutoHashMapUnmanaged(Id, []const Id) = .empty,
4044block_stack: std.ArrayList(*Block) = .empty,
4145block_label: Id = .none,
4246/// Whether the current block has been terminated by a terminator
......@@ -49,7 +53,21 @@ tracked_allocas: std.AutoHashMapUnmanaged(Id, ?Id) = .empty,
4953loop_switches: std.AutoHashMapUnmanaged(Air.Inst.Index, LoopSwitch) = .empty,
5054id_scratch: std.ArrayList(Id) = .empty,
5155
52const big_int_bits = @bitSizeOf(u32);
56fn hasInt64(target: *const std.Target) bool {
57 return target.cpu.arch == .spirv64 or target.cpu.has(.spirv, .int64);
58}
59
60fn bigIntBits(cg: *const CodeGen) u16 {
61 return if (hasInt64(cg.zcu.getTarget())) 64 else 32;
62}
63
64fn limbType(cg: *const CodeGen) Type {
65 return if (cg.bigIntBits() == 64) .u64 else .u32;
66}
67
68fn limbTypeId(cg: *CodeGen) !Id {
69 return cg.resolveType(cg.limbType(), .direct);
70}
5371
5472/// Data can be lowered into in two basic representations: indirect, which is when
5573/// a type is stored in memory, and direct, which is how a type is stored when its
......@@ -163,6 +181,7 @@ pub fn deinit(cg: *CodeGen) void {
163181 cg.block_stack.deinit(gpa);
164182 cg.block_results.deinit(gpa);
165183 cg.args.deinit(gpa);
184 cg.composite_limbs.deinit(gpa);
166185 cg.tracked_allocas.deinit(gpa);
167186 cg.inst_results.deinit(gpa);
168187 cg.loop_switches.deinit(gpa);
......@@ -407,7 +426,7 @@ pub fn addEntryPointDeps(
407426 cg: *CodeGen,
408427 decl_index: Decl.Index,
409428 seen: *std.bit_set.Dynamic,
410 interface: *std.array_list.Managed(Id),
429 interface: *std.ArrayList(Id),
411430) !void {
412431 const decl = cg.declPtr(decl_index);
413432 const deps = cg.decl_deps.items[decl.begin_dep..decl.end_dep];
......@@ -419,7 +438,7 @@ pub fn addEntryPointDeps(
419438 seen.set(@intFromEnum(decl_index));
420439
421440 if (decl.kind == .global) {
422 try interface.append(decl.result_id);
441 try interface.append(cg.gpa, decl.result_id);
423442 }
424443
425444 for (deps) |dep| {
......@@ -471,14 +490,14 @@ pub fn backingIntBits(cg: *const CodeGen, bits: u16) struct { u16, bool } {
471490 .{ .bits = 8, .enabled = target.cpu.has(.spirv, .int8) },
472491 .{ .bits = 16, .enabled = target.cpu.has(.spirv, .int16) },
473492 .{ .bits = 32, .enabled = true },
474 .{ .bits = 64, .enabled = target.cpu.has(.spirv, .int64) or target.cpu.arch == .spirv64 },
493 .{ .bits = 64, .enabled = hasInt64(target) },
475494 };
476495
477496 for (ints) |int| {
478497 if (bits <= int.bits and int.enabled) return .{ int.bits, false };
479498 }
480499
481 return .{ std.mem.alignForward(u16, bits, big_int_bits), true };
500 return .{ std.mem.alignForward(u16, bits, cg.bigIntBits()), true };
482501}
483502
484503pub fn intType(cg: *CodeGen, signedness: std.lang.Signedness, bits: u16) !Id {
......@@ -492,14 +511,16 @@ pub fn intType(cg: *CodeGen, signedness: std.lang.Signedness, bits: u16) !Id {
492511 };
493512 const backing_bits, const big_int = cg.backingIntBits(bits);
494513 if (big_int) {
495 const u32_ty = try cg.intType(.unsigned, 32);
514 const limb_bits = cg.bigIntBits();
515 const limb_ty = try cg.intType(.unsigned, limb_bits);
516 const len_ty = try cg.intType(.unsigned, 32);
496517 const len_id = cg.allocId();
497518 try cg.sections.globals.emit(cg.gpa, .OpConstant, .{
498 .id_result_type = u32_ty,
519 .id_result_type = len_ty,
499520 .id_result = len_id,
500 .value = .{ .uint32 = backing_bits / big_int_bits },
521 .value = .{ .uint32 = backing_bits / limb_bits },
501522 });
502 return cg.arrayType(len_id, u32_ty);
523 return cg.arrayType(len_id, limb_ty);
503524 }
504525
505526 const result_id = cg.allocId();
......@@ -1443,7 +1464,7 @@ fn constInt(cg: *CodeGen, ty: Type, value: anytype) !Id {
14431464 .signed => @bitCast(@as(i64, @intCast(value))),
14441465 .unsigned => @as(u64, @intCast(value)),
14451466 };
1446 const n_limbs = backing_bits / big_int_bits;
1467 const n_limbs = backing_bits / cg.bigIntBits();
14471468 const fill: u32 = if (signedness == .signed and value < 0) 0xFFFFFFFF else 0;
14481469 const scratch_top = cg.id_scratch.items.len;
14491470 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
......@@ -1616,21 +1637,33 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
16161637 const int_info = ty.intInfo(zcu);
16171638 const backing_bits, const is_big_int = cg.backingIntBits(int_info.bits);
16181639 if (is_big_int) {
1619 const n_limbs = backing_bits / big_int_bits;
1640 const limb_bits = cg.bigIntBits();
1641 const n_limbs = backing_bits / limb_bits;
16201642 const big_result_ty_id = try cg.resolveType(ty, .indirect);
16211643 var bigint_space: Value.BigIntSpace = undefined;
16221644 const bigint = val.toBigInt(&bigint_space, zcu);
1623 const limb_values = try gpa.alloc(u32, n_limbs);
1624 defer gpa.free(limb_values);
1625 bigint.writeTwosComplement(std.mem.sliceAsBytes(limb_values), .little);
1626 if (builtin.cpu.arch.endian() == .big) {
1627 for (limb_values) |*limb| limb.* = @byteSwap(limb.*);
1628 }
1645 const limb_bytes = try gpa.alloc(u8, backing_bits / 8);
1646 defer gpa.free(limb_bytes);
1647 bigint.writeTwosComplement(limb_bytes, .little);
16291648 const scratch_top = cg.id_scratch.items.len;
16301649 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
16311650 const constituents = try cg.id_scratch.addManyAsSlice(gpa, n_limbs);
1632 for (constituents, 0..) |*c, i| {
1633 c.* = try cg.constInt(.u32, limb_values[i]);
1651 switch (limb_bits) {
1652 32 => {
1653 const limbs_u32: []u32 = @ptrCast(@alignCast(limb_bytes));
1654 for (constituents, limbs_u32) |*c, v| {
1655 const host_v = if (builtin.cpu.arch.endian() == .big) @byteSwap(v) else v;
1656 c.* = try cg.constInt(.u32, host_v);
1657 }
1658 },
1659 64 => {
1660 const limbs_u64: []u64 = @ptrCast(@alignCast(limb_bytes));
1661 for (constituents, limbs_u64) |*c, v| {
1662 const host_v = if (builtin.cpu.arch.endian() == .big) @byteSwap(v) else v;
1663 c.* = try cg.constInt(.u64, host_v);
1664 }
1665 },
1666 else => unreachable,
16341667 }
16351668 break :cache try cg.constructComposite(big_result_ty_id, constituents);
16361669 }
......@@ -1776,11 +1809,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
17761809 const struct_type = zcu.typeToStruct(ty).?;
17771810 assert(struct_type.layout != .@"packed"); // packed structs use `bitpack`
17781811
1779 var types = std.array_list.Managed(Type).init(gpa);
1780 defer types.deinit();
1812 var types: std.ArrayList(Type) = .empty;
1813 defer types.deinit(gpa);
17811814
1782 var constituents = std.array_list.Managed(Id).init(gpa);
1783 defer constituents.deinit();
1815 var constituents: std.ArrayList(Id) = .empty;
1816 defer constituents.deinit(gpa);
17841817
17851818 var it = struct_type.iterateRuntimeOrder(ip);
17861819 while (it.next()) |field_index| {
......@@ -1794,8 +1827,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
17941827 const field_val = try val.fieldValue(pt, field_index);
17951828 const field_id = try cg.constant(field_ty, field_val, .indirect);
17961829
1797 try types.append(field_ty);
1798 try constituents.append(field_id);
1830 try types.append(gpa, field_ty);
1831 try constituents.append(gpa, field_id);
17991832 }
18001833
18011834 const comp_ty_id = try cg.resolveType(ty, .direct);
......@@ -2189,14 +2222,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
21892222 64 => target.cpu.has(.spirv, .float64),
21902223 else => false,
21912224 };
2192
2193 if (!supported) {
2194 return cg.fail(
2195 "floating point width of {} bits is not supported for the current SPIR-V feature set",
2196 .{bits},
2197 );
2198 }
2199
2225 if (!supported) return cg.fail(
2226 "'{f}' is not supported on the current SPIR-V feature set",
2227 .{ty.fmt(cg.pt)},
2228 );
22002229 return try cg.floatType(bits);
22012230 },
22022231 .array => {
......@@ -2288,7 +2317,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
22882317 }),
22892318 },
22902319 };
2291 const child_ty_id = try cg.resolveType(child_ty, .indirect);
2320 const child_ty_id = try cg.pointeeType(ptr_info.flags.address_space, child_ty, false);
22922321 const storage_class = cg.storageClass(ptr_info.flags.address_space);
22932322 const ptr_ty_id = try cg.ptrType(child_ty_id, storage_class);
22942323
......@@ -2336,11 +2365,11 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
23362365 return try cg.resolveType(.fromInterned(struct_type.packed_backing_int_type), .direct);
23372366 }
23382367
2339 var member_types = std.array_list.Managed(Id).init(gpa);
2340 defer member_types.deinit();
2368 var member_types: std.ArrayList(Id) = .empty;
2369 defer member_types.deinit(gpa);
23412370
2342 var member_names = std.array_list.Managed([]const u8).init(gpa);
2343 defer member_names.deinit();
2371 var member_names: std.ArrayList([]const u8) = .empty;
2372 defer member_names.deinit(gpa);
23442373
23452374 var it = struct_type.iterateRuntimeOrder(ip);
23462375 while (it.next()) |field_index| {
......@@ -2348,8 +2377,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
23482377 if (!field_ty.hasRuntimeBits(zcu)) continue;
23492378
23502379 const field_name = struct_type.field_names.get(ip)[field_index];
2351 try member_types.append(try cg.resolveType(field_ty, .indirect));
2352 try member_names.append(field_name.toSlice(ip));
2380 try member_types.append(gpa, try cg.resolveType(field_ty, .indirect));
2381 try member_names.append(gpa, field_name.toSlice(ip));
23532382 }
23542383
23552384 const result_id = try cg.structType(
......@@ -2766,20 +2795,28 @@ const CompositeInt = struct {
27662795 info: ArithmeticTypeInfo,
27672796
27682797 fn init(cg: *CodeGen, composite_id: Id, info: ArithmeticTypeInfo) !CompositeInt {
2769 const n_limbs: u16 = info.backing_bits / big_int_bits;
2798 const n_limbs: u16 = info.backing_bits / cg.bigIntBits();
27702799 const gpa = cg.gpa;
2771 const u32_ty_id = try cg.resolveType(.u32, .direct);
2800 if (cg.composite_limbs.get(composite_id)) |cached| {
2801 assert(cached.len == n_limbs);
2802 const limbs = try cg.id_scratch.addManyAsSlice(gpa, n_limbs);
2803 @memcpy(limbs, cached);
2804 return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info };
2805 }
2806 const limb_ty_id = try cg.limbTypeId();
27722807 const limbs = try cg.id_scratch.addManyAsSlice(gpa, n_limbs);
27732808 for (limbs, 0..) |*limb, i| {
27742809 const result_id = cg.allocId();
27752810 try cg.body.emit(gpa, .OpCompositeExtract, .{
2776 .id_result_type = u32_ty_id,
2811 .id_result_type = limb_ty_id,
27772812 .id_result = result_id,
27782813 .composite = composite_id,
27792814 .indexes = &.{@as(u32, @intCast(i))},
27802815 });
27812816 limb.* = result_id;
27822817 }
2818 const cached = try cg.arena.dupe(Id, limbs);
2819 try cg.composite_limbs.put(gpa, composite_id, cached);
27832820 return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info };
27842821 }
27852822
......@@ -2793,9 +2830,9 @@ const CompositeInt = struct {
27932830 }
27942831
27952832 fn zero(cg: *CodeGen, info: ArithmeticTypeInfo) !CompositeInt {
2796 const n_limbs: u16 = info.backing_bits / big_int_bits;
2833 const n_limbs: u16 = info.backing_bits / cg.bigIntBits();
27972834 const limbs = try cg.id_scratch.addManyAsSlice(cg.gpa, n_limbs);
2798 const zero_id = try cg.constInt(.u32, @as(u32, 0));
2835 const zero_id = try cg.constInt(cg.limbType(), @as(u64, 0));
27992836 for (limbs) |*limb| limb.* = zero_id;
28002837 return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info };
28012838 }
......@@ -2808,10 +2845,10 @@ const CompositeInt = struct {
28082845 fn limbBinOp(ci: CompositeInt, opcode: Opcode, lhs: Id, rhs: Id) !Id {
28092846 const cg = ci.cg;
28102847 const gpa = cg.gpa;
2811 const u32_ty_id = try cg.resolveType(.u32, .direct);
2848 const limb_ty_id = try cg.limbTypeId();
28122849 const result_id = cg.allocId();
28132850 try cg.body.emitRaw(gpa, opcode, 4);
2814 cg.body.writeOperand(Id, u32_ty_id);
2851 cg.body.writeOperand(Id, limb_ty_id);
28152852 cg.body.writeOperand(Id, result_id);
28162853 cg.body.writeOperand(Id, lhs);
28172854 cg.body.writeOperand(Id, rhs);
......@@ -2821,10 +2858,10 @@ const CompositeInt = struct {
28212858 fn limbUnOp(ci: CompositeInt, opcode: Opcode, operand: Id) !Id {
28222859 const cg = ci.cg;
28232860 const gpa = cg.gpa;
2824 const u32_ty_id = try cg.resolveType(.u32, .direct);
2861 const limb_ty_id = try cg.limbTypeId();
28252862 const result_id = cg.allocId();
28262863 try cg.body.emitRaw(gpa, opcode, 3);
2827 cg.body.writeOperand(Id, u32_ty_id);
2864 cg.body.writeOperand(Id, limb_ty_id);
28282865 cg.body.writeOperand(Id, result_id);
28292866 cg.body.writeOperand(Id, operand);
28302867 return result_id;
......@@ -2916,16 +2953,17 @@ const CompositeInt = struct {
29162953 var cmp_l = l;
29172954 var cmp_r = r;
29182955 if (use_signed) {
2919 const i32_ty_id = try cg.resolveType(.i32, .direct);
2956 const signed_limb_ty: Type = if (cg.bigIntBits() == 64) .i64 else .i32;
2957 const signed_limb_ty_id = try cg.resolveType(signed_limb_ty, .direct);
29202958 const sl = cg.allocId();
29212959 try cg.body.emit(gpa, .OpBitcast, .{
2922 .id_result_type = i32_ty_id,
2960 .id_result_type = signed_limb_ty_id,
29232961 .id_result = sl,
29242962 .operand = l,
29252963 });
29262964 const sr = cg.allocId();
29272965 try cg.body.emit(gpa, .OpBitcast, .{
2928 .id_result_type = i32_ty_id,
2966 .id_result_type = signed_limb_ty_id,
29292967 .id_result = sr,
29302968 .operand = r,
29312969 });
......@@ -2969,16 +3007,17 @@ const CompositeInt = struct {
29693007 const comp = zcu.comp;
29703008 const io = comp.io;
29713009
2972 const u32_zig = try pt.intType(.unsigned, 32);
2973 const u32_ty_id = try cg.resolveType(.u32, .direct);
3010 const limb_bits = cg.bigIntBits();
3011 const limb_zig = try pt.intType(.unsigned, limb_bits);
3012 const limb_ty_id = try cg.limbTypeId();
29743013 const carry_struct_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{
2975 .types = &.{ u32_zig.toIntern(), u32_zig.toIntern() },
3014 .types = &.{ limb_zig.toIntern(), limb_zig.toIntern() },
29763015 .values = &.{ .none, .none },
29773016 }));
29783017 const carry_struct_ty_id = try cg.resolveType(carry_struct_ty, .direct);
29793018
29803019 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs);
2981 var carry_id = try cg.constInt(.u32, @as(u32, 0));
3020 var carry_id = try cg.constInt(cg.limbType(), @as(u64, 0));
29823021
29833022 const opcode: Opcode = if (is_add) .OpIAddCarry else .OpISubBorrow;
29843023
......@@ -2992,14 +3031,14 @@ const CompositeInt = struct {
29923031
29933032 const sum1 = cg.allocId();
29943033 try cg.body.emit(gpa, .OpCompositeExtract, .{
2995 .id_result_type = u32_ty_id,
3034 .id_result_type = limb_ty_id,
29963035 .id_result = sum1,
29973036 .composite = op1,
29983037 .indexes = &.{0},
29993038 });
30003039 const carry1 = cg.allocId();
30013040 try cg.body.emit(gpa, .OpCompositeExtract, .{
3002 .id_result_type = u32_ty_id,
3041 .id_result_type = limb_ty_id,
30033042 .id_result = carry1,
30043043 .composite = op1,
30053044 .indexes = &.{1},
......@@ -3014,14 +3053,14 @@ const CompositeInt = struct {
30143053
30153054 result_limbs[i] = cg.allocId();
30163055 try cg.body.emit(gpa, .OpCompositeExtract, .{
3017 .id_result_type = u32_ty_id,
3056 .id_result_type = limb_ty_id,
30183057 .id_result = result_limbs[i],
30193058 .composite = op2,
30203059 .indexes = &.{0},
30213060 });
30223061 const carry2 = cg.allocId();
30233062 try cg.body.emit(gpa, .OpCompositeExtract, .{
3024 .id_result_type = u32_ty_id,
3063 .id_result_type = limb_ty_id,
30253064 .id_result = carry2,
30263065 .composite = op2,
30273066 .indexes = &.{1},
......@@ -3036,16 +3075,18 @@ const CompositeInt = struct {
30363075 fn shl(ci: CompositeInt, shift_amt_id: Id) !CompositeInt {
30373076 const cg = ci.cg;
30383077 const gpa = cg.gpa;
3039 const u32_ty_id = try cg.resolveType(.u32, .direct);
3078 const limb_bits = cg.bigIntBits();
3079 const limb_ty = cg.limbType();
3080 const limb_ty_id = try cg.limbTypeId();
30403081 const bool_ty_id = try cg.resolveType(.bool, .direct);
3041 const zero_id = try cg.constInt(.u32, @as(u32, 0));
3042 const five_id = try cg.constInt(.u32, @as(u32, 5));
3043 const thirty_one_id = try cg.constInt(.u32, @as(u32, 31));
3044 const thirty_two_id = try cg.constInt(.u32, @as(u32, 32));
3045
3046 const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, five_id);
3047 const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, thirty_one_id);
3048 const comp_frac = try ci.limbBinOp(.OpISub, thirty_two_id, frac);
3082 const zero_id = try cg.constInt(limb_ty, @as(u64, 0));
3083 const log2_bits_id = try cg.constInt(limb_ty, @as(u64, std.math.log2_int(u16, limb_bits)));
3084 const bits_minus_1_id = try cg.constInt(limb_ty, @as(u64, limb_bits - 1));
3085 const bits_id = try cg.constInt(limb_ty, @as(u64, limb_bits));
3086
3087 const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, log2_bits_id);
3088 const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, bits_minus_1_id);
3089 const comp_frac = try ci.limbBinOp(.OpISub, bits_id, frac);
30493090 const frac_is_zero = blk: {
30503091 const r = cg.allocId();
30513092 try cg.body.emit(gpa, .OpIEqual, .{
......@@ -3060,12 +3101,12 @@ const CompositeInt = struct {
30603101 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs);
30613102
30623103 for (0..ci.n_limbs) |i| {
3063 const i_id = try cg.constInt(.u32, @as(u32, @intCast(i)));
3104 const i_id = try cg.constInt(limb_ty, @as(u64, @intCast(i)));
30643105 var main_val = zero_id;
30653106 var carry_val = zero_id;
30663107
30673108 for (0..ci.n_limbs) |j| {
3068 const j_id = try cg.constInt(.u32, @as(u32, @intCast(j)));
3109 const j_id = try cg.constInt(limb_ty, @as(u64, @intCast(j)));
30693110 const j_plus_whole = try ci.limbBinOp(.OpIAdd, j_id, whole);
30703111
30713112 const is_main = blk: {
......@@ -3082,7 +3123,7 @@ const CompositeInt = struct {
30823123 main_val = blk: {
30833124 const r = cg.allocId();
30843125 try cg.body.emit(gpa, .OpSelect, .{
3085 .id_result_type = u32_ty_id,
3126 .id_result_type = limb_ty_id,
30863127 .id_result = r,
30873128 .condition = is_main,
30883129 .object_1 = shifted,
......@@ -3091,7 +3132,7 @@ const CompositeInt = struct {
30913132 break :blk r;
30923133 };
30933134
3094 const one_id = try cg.constInt(.u32, @as(u32, 1));
3135 const one_id = try cg.constInt(limb_ty, @as(u64, 1));
30953136 const j_plus_whole_plus_1 = try ci.limbBinOp(.OpIAdd, j_plus_whole, one_id);
30963137 const is_carry = blk: {
30973138 const r = cg.allocId();
......@@ -3107,7 +3148,7 @@ const CompositeInt = struct {
31073148 const guarded_carry = blk: {
31083149 const r = cg.allocId();
31093150 try cg.body.emit(gpa, .OpSelect, .{
3110 .id_result_type = u32_ty_id,
3151 .id_result_type = limb_ty_id,
31113152 .id_result = r,
31123153 .condition = frac_is_zero,
31133154 .object_1 = zero_id,
......@@ -3118,7 +3159,7 @@ const CompositeInt = struct {
31183159 carry_val = blk: {
31193160 const r = cg.allocId();
31203161 try cg.body.emit(gpa, .OpSelect, .{
3121 .id_result_type = u32_ty_id,
3162 .id_result_type = limb_ty_id,
31223163 .id_result = r,
31233164 .condition = is_carry,
31243165 .object_1 = guarded_carry,
......@@ -3137,16 +3178,18 @@ const CompositeInt = struct {
31373178 fn shr(ci: CompositeInt, shift_amt_id: Id, comptime is_arithmetic: bool) !CompositeInt {
31383179 const cg = ci.cg;
31393180 const gpa = cg.gpa;
3140 const u32_ty_id = try cg.resolveType(.u32, .direct);
3181 const limb_bits = cg.bigIntBits();
3182 const limb_ty = cg.limbType();
3183 const limb_ty_id = try cg.limbTypeId();
31413184 const bool_ty_id = try cg.resolveType(.bool, .direct);
3142 const zero_id = try cg.constInt(.u32, @as(u32, 0));
3143 const five_id = try cg.constInt(.u32, @as(u32, 5));
3144 const thirty_one_id = try cg.constInt(.u32, @as(u32, 31));
3145 const thirty_two_id = try cg.constInt(.u32, @as(u32, 32));
3146
3147 const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, five_id);
3148 const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, thirty_one_id);
3149 const comp_frac = try ci.limbBinOp(.OpISub, thirty_two_id, frac);
3185 const zero_id = try cg.constInt(limb_ty, @as(u64, 0));
3186 const log2_bits_id = try cg.constInt(limb_ty, @as(u64, std.math.log2_int(u16, limb_bits)));
3187 const bits_minus_1_id = try cg.constInt(limb_ty, @as(u64, limb_bits - 1));
3188 const bits_id = try cg.constInt(limb_ty, @as(u64, limb_bits));
3189
3190 const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, log2_bits_id);
3191 const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, bits_minus_1_id);
3192 const comp_frac = try ci.limbBinOp(.OpISub, bits_id, frac);
31503193 const frac_is_zero = blk: {
31513194 const r = cg.allocId();
31523195 try cg.body.emit(gpa, .OpIEqual, .{
......@@ -3159,24 +3202,25 @@ const CompositeInt = struct {
31593202 };
31603203
31613204 const fill_id = if (is_arithmetic) blk: {
3162 const i32_ty_id = try cg.resolveType(.i32, .direct);
3205 const signed_limb_ty: Type = if (limb_bits == 64) .i64 else .i32;
3206 const signed_limb_ty_id = try cg.resolveType(signed_limb_ty, .direct);
31633207 const msb_signed = cg.allocId();
31643208 try cg.body.emit(gpa, .OpBitcast, .{
3165 .id_result_type = i32_ty_id,
3209 .id_result_type = signed_limb_ty_id,
31663210 .id_result = msb_signed,
31673211 .operand = ci.limbs[ci.n_limbs - 1],
31683212 });
3169 const shift31 = try cg.constInt(.i32, @as(i32, 31));
3213 const shift_amt = try cg.constInt(signed_limb_ty, @as(u64, limb_bits - 1));
31703214 const sign_ext = cg.allocId();
31713215 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
3172 .id_result_type = i32_ty_id,
3216 .id_result_type = signed_limb_ty_id,
31733217 .id_result = sign_ext,
31743218 .base = msb_signed,
3175 .shift = shift31,
3219 .shift = shift_amt,
31763220 });
31773221 const back = cg.allocId();
31783222 try cg.body.emit(gpa, .OpBitcast, .{
3179 .id_result_type = u32_ty_id,
3223 .id_result_type = limb_ty_id,
31803224 .id_result = back,
31813225 .operand = sign_ext,
31823226 });
......@@ -3189,7 +3233,7 @@ const CompositeInt = struct {
31893233 const shifted_fill = try ci.limbBinOp(.OpShiftLeftLogical, fill_id, comp_frac);
31903234 const guarded = cg.allocId();
31913235 try cg.body.emit(gpa, .OpSelect, .{
3192 .id_result_type = u32_ty_id,
3236 .id_result_type = limb_ty_id,
31933237 .id_result = guarded,
31943238 .condition = frac_is_zero,
31953239 .object_1 = zero_id,
......@@ -3199,12 +3243,12 @@ const CompositeInt = struct {
31993243 } else zero_id;
32003244
32013245 for (0..ci.n_limbs) |i| {
3202 const i_id = try cg.constInt(.u32, @as(u32, @intCast(i)));
3246 const i_id = try cg.constInt(limb_ty, @as(u64, @intCast(i)));
32033247 var main_val = fill_id;
32043248 var carry_val = arith_carry_init;
32053249
32063250 for (0..ci.n_limbs) |j| {
3207 const j_id = try cg.constInt(.u32, @as(u32, @intCast(j)));
3251 const j_id = try cg.constInt(limb_ty, @as(u64, @intCast(j)));
32083252 const i_plus_whole = try ci.limbBinOp(.OpIAdd, i_id, whole);
32093253 const is_main = blk: {
32103254 const r = cg.allocId();
......@@ -3220,7 +3264,7 @@ const CompositeInt = struct {
32203264 main_val = blk: {
32213265 const r = cg.allocId();
32223266 try cg.body.emit(gpa, .OpSelect, .{
3223 .id_result_type = u32_ty_id,
3267 .id_result_type = limb_ty_id,
32243268 .id_result = r,
32253269 .condition = is_main,
32263270 .object_1 = shifted,
......@@ -3229,7 +3273,7 @@ const CompositeInt = struct {
32293273 break :blk r;
32303274 };
32313275
3232 const one_id = try cg.constInt(.u32, @as(u32, 1));
3276 const one_id = try cg.constInt(limb_ty, @as(u64, 1));
32333277 const i_plus_whole_plus_1 = try ci.limbBinOp(.OpIAdd, i_plus_whole, one_id);
32343278 const is_carry = blk: {
32353279 const r = cg.allocId();
......@@ -3245,7 +3289,7 @@ const CompositeInt = struct {
32453289 const guarded_carry = blk: {
32463290 const r = cg.allocId();
32473291 try cg.body.emit(gpa, .OpSelect, .{
3248 .id_result_type = u32_ty_id,
3292 .id_result_type = limb_ty_id,
32493293 .id_result = r,
32503294 .condition = frac_is_zero,
32513295 .object_1 = zero_id,
......@@ -3256,7 +3300,7 @@ const CompositeInt = struct {
32563300 carry_val = blk: {
32573301 const r = cg.allocId();
32583302 try cg.body.emit(gpa, .OpSelect, .{
3259 .id_result_type = u32_ty_id,
3303 .id_result_type = limb_ty_id,
32603304 .id_result = r,
32613305 .condition = is_carry,
32623306 .object_1 = guarded_carry,
......@@ -3284,17 +3328,18 @@ const CompositeInt = struct {
32843328
32853329 const n: usize = ci.n_limbs;
32863330 const total: usize = if (wide) 2 * n else n;
3287 const u32_zig = try pt.intType(.unsigned, 32);
3288 const u32_ty_id = try cg.resolveType(.u32, .direct);
3331 const limb_bits = cg.bigIntBits();
3332 const limb_zig = try pt.intType(.unsigned, limb_bits);
3333 const limb_ty_id = try cg.limbTypeId();
32893334
32903335 const pair_struct_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{
3291 .types = &.{ u32_zig.toIntern(), u32_zig.toIntern() },
3336 .types = &.{ limb_zig.toIntern(), limb_zig.toIntern() },
32923337 .values = &.{ .none, .none },
32933338 }));
32943339 const pair_struct_ty_id = try cg.resolveType(pair_struct_ty, .direct);
32953340
32963341 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, total);
3297 const zero_id = try cg.constInt(.u32, @as(u32, 0));
3342 const zero_id = try cg.constInt(cg.limbType(), @as(u64, 0));
32983343 for (result_limbs) |*r| r.* = zero_id;
32993344
33003345 for (0..n) |i| {
......@@ -3309,7 +3354,7 @@ const CompositeInt = struct {
33093354 .opencl => {
33103355 lo = cg.allocId();
33113356 try cg.body.emit(gpa, .OpIMul, .{
3312 .id_result_type = u32_ty_id,
3357 .id_result_type = limb_ty_id,
33133358 .id_result = lo,
33143359 .operand_1 = ci.limbs[i],
33153360 .operand_2 = other.limbs[j],
......@@ -3318,7 +3363,7 @@ const CompositeInt = struct {
33183363 const set = try cg.importExtendedSet();
33193364 hi = cg.allocId();
33203365 try cg.body.emit(gpa, .OpExtInst, .{
3321 .id_result_type = u32_ty_id,
3366 .id_result_type = limb_ty_id,
33223367 .id_result = hi,
33233368 .set = set,
33243369 .instruction = .{ .inst = @intFromEnum(spec.OpenClOpcode.u_mul_hi) },
......@@ -3336,14 +3381,14 @@ const CompositeInt = struct {
33363381
33373382 lo = cg.allocId();
33383383 try cg.body.emit(gpa, .OpCompositeExtract, .{
3339 .id_result_type = u32_ty_id,
3384 .id_result_type = limb_ty_id,
33403385 .id_result = lo,
33413386 .composite = mul_result,
33423387 .indexes = &.{0},
33433388 });
33443389 hi = cg.allocId();
33453390 try cg.body.emit(gpa, .OpCompositeExtract, .{
3346 .id_result_type = u32_ty_id,
3391 .id_result_type = limb_ty_id,
33473392 .id_result = hi,
33483393 .composite = mul_result,
33493394 .indexes = &.{1},
......@@ -3361,14 +3406,14 @@ const CompositeInt = struct {
33613406
33623407 const sum1 = cg.allocId();
33633408 try cg.body.emit(gpa, .OpCompositeExtract, .{
3364 .id_result_type = u32_ty_id,
3409 .id_result_type = limb_ty_id,
33653410 .id_result = sum1,
33663411 .composite = add1,
33673412 .indexes = &.{0},
33683413 });
33693414 const c1 = cg.allocId();
33703415 try cg.body.emit(gpa, .OpCompositeExtract, .{
3371 .id_result_type = u32_ty_id,
3416 .id_result_type = limb_ty_id,
33723417 .id_result = c1,
33733418 .composite = add1,
33743419 .indexes = &.{1},
......@@ -3384,14 +3429,14 @@ const CompositeInt = struct {
33843429
33853430 result_limbs[k] = cg.allocId();
33863431 try cg.body.emit(gpa, .OpCompositeExtract, .{
3387 .id_result_type = u32_ty_id,
3432 .id_result_type = limb_ty_id,
33883433 .id_result = result_limbs[k],
33893434 .composite = add2,
33903435 .indexes = &.{0},
33913436 });
33923437 const c2 = cg.allocId();
33933438 try cg.body.emit(gpa, .OpCompositeExtract, .{
3394 .id_result_type = u32_ty_id,
3439 .id_result_type = limb_ty_id,
33953440 .id_result = c2,
33963441 .composite = add2,
33973442 .indexes = &.{1},
......@@ -3412,7 +3457,8 @@ const CompositeInt = struct {
34123457 if (ci.info.bits == ci.info.backing_bits) return ci;
34133458 const cg = ci.cg;
34143459 const gpa = cg.gpa;
3415 const top_bits: u16 = ci.info.bits % big_int_bits;
3460 const limb_bits = cg.bigIntBits();
3461 const top_bits: u16 = ci.info.bits % limb_bits;
34163462 assert(top_bits != 0);
34173463
34183464 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs);
......@@ -3421,41 +3467,43 @@ const CompositeInt = struct {
34213467 }
34223468
34233469 const top_limb = ci.limbs[ci.n_limbs - 1];
3470 const limb_ty = cg.limbType();
3471 const limb_signed_ty: Type = if (limb_bits == 64) .i64 else .i32;
34243472 switch (ci.info.signedness) {
34253473 .unsigned => {
3426 const mask_val: u32 = (@as(u32, 1) << @as(u5, @intCast(top_bits))) - 1;
3427 const mask_id = try cg.constInt(.u32, mask_val);
3474 const mask_val: u64 = (@as(u64, 1) << @as(u6, @intCast(top_bits))) - 1;
3475 const mask_id = try cg.constInt(limb_ty, mask_val);
34283476 result_limbs[ci.n_limbs - 1] = try ci.limbBinOp(.OpBitwiseAnd, top_limb, mask_id);
34293477 },
34303478 .signed => {
3431 const u32_ty_id = try cg.resolveType(.u32, .direct);
3432 const i32_ty_id = try cg.resolveType(.i32, .direct);
3433 const shift_amt: u32 = 32 - top_bits;
3434 const shift_id = try cg.constInt(.u32, shift_amt);
3479 const limb_ty_id = try cg.limbTypeId();
3480 const signed_ty_id = try cg.resolveType(limb_signed_ty, .direct);
3481 const shift_amt: u32 = @intCast(limb_bits - top_bits);
3482 const shift_id = try cg.constInt(limb_ty, shift_amt);
34353483
34363484 const as_signed = cg.allocId();
34373485 try cg.body.emit(gpa, .OpBitcast, .{
3438 .id_result_type = i32_ty_id,
3486 .id_result_type = signed_ty_id,
34393487 .id_result = as_signed,
34403488 .operand = top_limb,
34413489 });
34423490 const shifted_left = cg.allocId();
34433491 try cg.body.emit(gpa, .OpShiftLeftLogical, .{
3444 .id_result_type = i32_ty_id,
3492 .id_result_type = signed_ty_id,
34453493 .id_result = shifted_left,
34463494 .base = as_signed,
34473495 .shift = shift_id,
34483496 });
34493497 const shifted_right = cg.allocId();
34503498 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
3451 .id_result_type = i32_ty_id,
3499 .id_result_type = signed_ty_id,
34523500 .id_result = shifted_right,
34533501 .base = shifted_left,
34543502 .shift = shift_id,
34553503 });
34563504 const back = cg.allocId();
34573505 try cg.body.emit(gpa, .OpBitcast, .{
3458 .id_result_type = u32_ty_id,
3506 .id_result_type = limb_ty_id,
34593507 .id_result = back,
34603508 .operand = shifted_right,
34613509 });
......@@ -4218,13 +4266,16 @@ const MemoryOptions = struct {
42184266fn needsLayout(cg: *CodeGen, as: std.lang.AddressSpace, pointee_ty: Type) bool {
42194267 const target = cg.zcu.getTarget();
42204268 if (target.os.tag != .vulkan and target.os.tag != .opengl) return false;
4221 switch (as) {
4222 .uniform, .push_constant, .storage_buffer => {},
4223 else => return false,
4224 }
4225 return switch (pointee_ty.zigTypeTag(cg.zcu)) {
4226 .@"struct", .@"union", .array => true,
4227 .spirv => pointee_ty.isSpirvRuntimeArray(cg.zcu),
4269 return switch (as) {
4270 .uniform,
4271 .push_constant,
4272 .storage_buffer,
4273 .physical_storage_buffer,
4274 => switch (pointee_ty.zigTypeTag(cg.zcu)) {
4275 .@"struct", .@"union", .array => true,
4276 .spirv => pointee_ty.isSpirvRuntimeArray(cg.zcu),
4277 else => false,
4278 },
42284279 else => false,
42294280 };
42304281}
......@@ -4537,10 +4588,6 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode
45374588 const zcu = cg.zcu;
45384589 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
45394590
4540 if (cg.typeOf(bin_op.lhs).isVector(zcu) and !cg.typeOf(bin_op.rhs).isVector(zcu)) {
4541 return cg.fail("vector shift with scalar rhs", .{});
4542 }
4543
45444591 const base = try cg.temporary(bin_op.lhs);
45454592 const shift = try cg.temporary(bin_op.rhs);
45464593
......@@ -4550,13 +4597,14 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode
45504597 switch (info.class) {
45514598 .composite_integer => {
45524599 const shift_info = cg.arithmeticTypeInfo(shift.ty);
4600 const limb_ty = cg.limbType();
45534601 const shift_amt_id = switch (shift_info.class) {
45544602 .composite_integer => blk: {
45554603 const shift_id = try shift.materialize(cg);
4556 const u32_ty_id = try cg.resolveType(.u32, .direct);
4604 const limb_ty_id = try cg.limbTypeId();
45574605 const result_id = cg.allocId();
45584606 try cg.body.emit(cg.gpa, .OpCompositeExtract, .{
4559 .id_result_type = u32_ty_id,
4607 .id_result_type = limb_ty_id,
45604608 .id_result = result_id,
45614609 .composite = shift_id,
45624610 .indexes = &.{@as(u32, 0)},
......@@ -4564,7 +4612,7 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode
45644612 break :blk result_id;
45654613 },
45664614 else => blk: {
4567 const converted = try cg.buildConvert(.u32, shift);
4615 const converted = try cg.buildConvert(limb_ty, shift);
45684616 break :blk try converted.materialize(cg);
45694617 },
45704618 };
......@@ -4885,12 +4933,12 @@ fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
48854933 const is_neg = try ci.cmp(ci_z, .lt);
48864934 const ci_neg = try ci_z.addSub(ci, false);
48874935 const result_info = cg.arithmeticTypeInfo(result_ty);
4888 const u32_ty_id = try cg.resolveType(.u32, .direct);
4936 const limb_ty_id = try cg.limbTypeId();
48894937 const result_limbs = try cg.id_scratch.addManyAsSlice(cg.gpa, ci.n_limbs);
48904938 for (0..ci.n_limbs) |i| {
48914939 result_limbs[i] = cg.allocId();
48924940 try cg.body.emit(cg.gpa, .OpSelect, .{
4893 .id_result_type = u32_ty_id,
4941 .id_result_type = limb_ty_id,
48944942 .id_result = result_limbs[i],
48954943 .condition = is_neg,
48964944 .object_1 = ci_neg.limbs[i],
......@@ -5064,12 +5112,13 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
50645112 const high_limbs = wide_limbs[ci_lhs2.n_limbs..];
50655113
50665114 const bool_ty_id = try cg.resolveType(.bool, .direct);
5067 const u32_ty_id = try cg.resolveType(.u32, .direct);
5068 const n: usize = info.backing_bits / big_int_bits;
5115 const limb_ty_id = try cg.limbTypeId();
5116 const limb_ty = cg.limbType();
5117 const n: usize = info.backing_bits / cg.bigIntBits();
50695118
50705119 const ov_bool = switch (info.signedness) {
50715120 .unsigned => blk: {
5072 const zero_id = try cg.constInt(.u32, @as(u32, 0));
5121 const zero_id = try cg.constInt(limb_ty, @as(u64, 0));
50735122 var any_nonzero = cg.allocId();
50745123 try cg.body.emit(gpa, .OpINotEqual, .{
50755124 .id_result_type = bool_ty_id,
......@@ -5102,32 +5151,33 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
51025151 .signed => blk: {
51035152 const ci_res = try CompositeInt.init(cg, result_val_id, info);
51045153 const top_limb = ci_res.limbs[n - 1];
5105 const i32_ty_id = try cg.resolveType(.i32, .direct);
5154 const signed_limb_ty: Type = if (cg.bigIntBits() == 64) .i64 else .i32;
5155 const signed_limb_ty_id = try cg.resolveType(signed_limb_ty, .direct);
51065156
5107 const top_bits: u16 = if (info.bits % big_int_bits == 0)
5108 big_int_bits
5157 const top_bits: u16 = if (info.bits % cg.bigIntBits() == 0)
5158 cg.bigIntBits()
51095159 else
5110 info.bits % big_int_bits;
5160 info.bits % cg.bigIntBits();
51115161
5112 const shift_amt: u32 = top_bits - 1;
5113 const shift_id = try cg.constInt(.u32, shift_amt);
5162 const shift_amt: u64 = top_bits - 1;
5163 const shift_id = try cg.constInt(limb_ty, shift_amt);
51145164
51155165 const as_signed = cg.allocId();
51165166 try cg.body.emit(gpa, .OpBitcast, .{
5117 .id_result_type = i32_ty_id,
5167 .id_result_type = signed_limb_ty_id,
51185168 .id_result = as_signed,
51195169 .operand = top_limb,
51205170 });
51215171 const sign_ext = cg.allocId();
51225172 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
5123 .id_result_type = i32_ty_id,
5173 .id_result_type = signed_limb_ty_id,
51245174 .id_result = sign_ext,
51255175 .base = as_signed,
51265176 .shift = shift_id,
51275177 });
51285178 const expected = cg.allocId();
51295179 try cg.body.emit(gpa, .OpBitcast, .{
5130 .id_result_type = u32_ty_id,
5180 .id_result_type = limb_ty_id,
51315181 .id_result = expected,
51325182 .operand = sign_ext,
51335183 });
......@@ -5160,25 +5210,25 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
51605210 }
51615211
51625212 if (info.bits != info.backing_bits) {
5163 const top_bits_s: u16 = info.bits % big_int_bits;
5164 const s_shift_id = try cg.constInt(.u32, top_bits_s - 1);
5213 const top_bits_s: u16 = info.bits % cg.bigIntBits();
5214 const s_shift_id = try cg.constInt(limb_ty, @as(u64, top_bits_s - 1));
51655215
51665216 const top_as_signed = cg.allocId();
51675217 try cg.body.emit(gpa, .OpBitcast, .{
5168 .id_result_type = i32_ty_id,
5218 .id_result_type = signed_limb_ty_id,
51695219 .id_result = top_as_signed,
51705220 .operand = top_limb,
51715221 });
51725222 const top_sign_ext = cg.allocId();
51735223 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
5174 .id_result_type = i32_ty_id,
5224 .id_result_type = signed_limb_ty_id,
51755225 .id_result = top_sign_ext,
51765226 .base = top_as_signed,
51775227 .shift = s_shift_id,
51785228 });
51795229 const top_expected = cg.allocId();
51805230 try cg.body.emit(gpa, .OpBitcast, .{
5181 .id_result_type = u32_ty_id,
5231 .id_result_type = limb_ty_id,
51825232 .id_result = top_expected,
51835233 .operand = top_sign_ext,
51845234 });
......@@ -5219,7 +5269,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
52195269 // of the result too.
52205270
52215271 const target = cg.zcu.getTarget();
5222 const largest_int_bits: u16 = if (target.cpu.has(.spirv, .int64) or target.cpu.arch == .spirv64) 64 else 32;
5272 const largest_int_bits: u16 = if (hasInt64(target)) 64 else 32;
52235273 // If non-null, the number of bits that the multiplication should be performed in. If
52245274 // null, we have to use wide multiplication.
52255275 const maybe_op_ty_bits: ?u16 = switch (info.bits) {
......@@ -5368,10 +5418,6 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
53685418 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
53695419 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
53705420
5371 if (cg.typeOf(extra.lhs).isVector(zcu) and !cg.typeOf(extra.rhs).isVector(zcu)) {
5372 return cg.fail("vector shift with scalar rhs", .{});
5373 }
5374
53755421 const base = try cg.temporary(extra.lhs);
53765422 const shift = try cg.temporary(extra.rhs);
53775423
......@@ -5964,7 +6010,27 @@ fn bitCast(
59646010
59656011 if (src_ty.toIntern() == dst_ty.toIntern()) return src_id;
59666012 if (src_ty.isPtrAtRuntime(zcu) and dst_ty.isPtrAtRuntime(zcu)) switch (target.os.tag) {
5967 .vulkan, .opengl => if (src_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) return src_id,
6013 .vulkan, .opengl => if (src_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) {
6014 const src_child = src_ty.childType(zcu);
6015 const dst_child = dst_ty.childType(zcu);
6016 if (!dst_child.hasRuntimeBits(zcu)) return src_id;
6017 if (src_child.toIntern() == dst_child.toIntern()) return src_id;
6018 if (src_ty.ptrInfo(zcu).packed_offset.host_size != 0 or
6019 dst_ty.ptrInfo(zcu).packed_offset.host_size != 0) return src_id;
6020
6021 var indices: std.ArrayList(u32) = .empty;
6022 defer indices.deinit(gpa);
6023 var cur = src_child;
6024 while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) {
6025 cur = switch (cur.zigTypeTag(zcu)) {
6026 .array, .vector => cur.childType(zcu),
6027 .@"struct" => cur.fieldType(0, zcu),
6028 else => unreachable,
6029 };
6030 }
6031 const dst_ty_id = try cg.resolveType(dst_ty, .direct);
6032 return try cg.accessChain(dst_ty_id, src_id, indices.items);
6033 },
59686034 else => {},
59696035 };
59706036
......@@ -6095,15 +6161,17 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
60956161
60966162 if (src_composite and dst_composite) {
60976163 const src_id = try src.materialize(cg);
6098 const src_n: u16 = src_info.backing_bits / big_int_bits;
6099 const dst_n: u16 = dst_info.backing_bits / big_int_bits;
6164 const limb_bits = cg.bigIntBits();
6165 const limb_ty = cg.limbType();
6166 const limb_ty_id = try cg.limbTypeId();
6167 const src_n: u16 = src_info.backing_bits / limb_bits;
6168 const dst_n: u16 = dst_info.backing_bits / limb_bits;
61006169 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, dst_n);
61016170 const min_n = @min(src_n, dst_n);
6102 const u32_ty_id = try cg.resolveType(.u32, .direct);
61036171 for (0..min_n) |i| {
61046172 result_limbs[i] = cg.allocId();
61056173 try cg.body.emit(gpa, .OpCompositeExtract, .{
6106 .id_result_type = u32_ty_id,
6174 .id_result_type = limb_ty_id,
61076175 .id_result = result_limbs[i],
61086176 .composite = src_id,
61096177 .indexes = &.{@as(u32, @intCast(i))},
......@@ -6111,30 +6179,31 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
61116179 }
61126180 if (dst_n > src_n) {
61136181 const fill = if (src_info.signedness == .signed) blk: {
6114 const i32_ty_id = try cg.resolveType(.i32, .direct);
6182 const signed_limb_ty: Type = if (limb_bits == 64) .i64 else .i32;
6183 const signed_limb_ty_id = try cg.resolveType(signed_limb_ty, .direct);
61156184 const msb = result_limbs[src_n - 1];
61166185 const msb_signed = cg.allocId();
61176186 try cg.body.emit(gpa, .OpBitcast, .{
6118 .id_result_type = i32_ty_id,
6187 .id_result_type = signed_limb_ty_id,
61196188 .id_result = msb_signed,
61206189 .operand = msb,
61216190 });
6122 const shift31 = try cg.constInt(.i32, @as(i32, 31));
6191 const shift_amt = try cg.constInt(signed_limb_ty, @as(u64, limb_bits - 1));
61236192 const sign_ext = cg.allocId();
61246193 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
6125 .id_result_type = i32_ty_id,
6194 .id_result_type = signed_limb_ty_id,
61266195 .id_result = sign_ext,
61276196 .base = msb_signed,
6128 .shift = shift31,
6197 .shift = shift_amt,
61296198 });
61306199 const back = cg.allocId();
61316200 try cg.body.emit(gpa, .OpBitcast, .{
6132 .id_result_type = u32_ty_id,
6201 .id_result_type = limb_ty_id,
61336202 .id_result = back,
61346203 .operand = sign_ext,
61356204 });
61366205 break :blk back;
6137 } else try cg.constInt(.u32, @as(u32, 0));
6206 } else try cg.constInt(limb_ty, @as(u64, 0));
61386207 for (min_n..dst_n) |i| {
61396208 result_limbs[i] = fill;
61406209 }
......@@ -6144,16 +6213,18 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
61446213 return try normalized.materialize(dst_ty);
61456214 } else if (src_composite and !dst_composite) {
61466215 const src_id = try src.materialize(cg);
6147 const u32_ty_id = try cg.resolveType(.u32, .direct);
6148 if (dst_info.backing_bits <= 32) {
6216 const limb_bits = cg.bigIntBits();
6217 const limb_ty = cg.limbType();
6218 const limb_ty_id = try cg.limbTypeId();
6219 if (dst_info.backing_bits <= limb_bits) {
61496220 const limb0 = cg.allocId();
61506221 try cg.body.emit(gpa, .OpCompositeExtract, .{
6151 .id_result_type = u32_ty_id,
6222 .id_result_type = limb_ty_id,
61526223 .id_result = limb0,
61536224 .composite = src_id,
61546225 .indexes = &.{@as(u32, 0)},
61556226 });
6156 const tmp: Temporary = .init(.u32, limb0);
6227 const tmp: Temporary = .init(limb_ty, limb0);
61576228 const converted = try cg.buildConvert(dst_ty, tmp);
61586229 const result = if (dst_info.bits < src_info.bits)
61596230 try cg.normalize(converted, dst_info)
......@@ -6161,16 +6232,17 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
61616232 converted;
61626233 return try result.materialize(cg);
61636234 } else {
6235 assert(limb_bits == 32); // dst > 64 while limbs are 64 shouldn't happen — dst fits in one 64-bit limb.
61646236 const limb0 = cg.allocId();
61656237 try cg.body.emit(gpa, .OpCompositeExtract, .{
6166 .id_result_type = u32_ty_id,
6238 .id_result_type = limb_ty_id,
61676239 .id_result = limb0,
61686240 .composite = src_id,
61696241 .indexes = &.{@as(u32, 0)},
61706242 });
61716243 const limb1 = cg.allocId();
61726244 try cg.body.emit(gpa, .OpCompositeExtract, .{
6173 .id_result_type = u32_ty_id,
6245 .id_result_type = limb_ty_id,
61746246 .id_result = limb1,
61756247 .composite = src_id,
61766248 .indexes = &.{@as(u32, 1)},
......@@ -6212,19 +6284,21 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
62126284 return try result.materialize(cg);
62136285 }
62146286 } else {
6215 const dst_n: u16 = dst_info.backing_bits / big_int_bits;
6287 const limb_bits = cg.bigIntBits();
6288 const limb_ty = cg.limbType();
6289 const limb_ty_id = try cg.limbTypeId();
6290 const dst_n: u16 = dst_info.backing_bits / limb_bits;
62166291 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, dst_n);
6217 const u32_ty_id = try cg.resolveType(.u32, .direct);
62186292
6219 if (src_info.backing_bits <= 32) {
6220 const converted = try cg.buildConvert(.u32, src);
6293 if (src_info.backing_bits <= limb_bits) {
6294 const converted = try cg.buildConvert(limb_ty, src);
62216295 result_limbs[0] = try converted.materialize(cg);
62226296 } else {
62236297 const src_as_u64 = try cg.buildConvert(.u64, src);
62246298 const src_id = try src_as_u64.materialize(cg);
62256299 result_limbs[0] = cg.allocId();
62266300 try cg.body.emit(gpa, .OpUConvert, .{
6227 .id_result_type = u32_ty_id,
6301 .id_result_type = limb_ty_id,
62286302 .id_result = result_limbs[0],
62296303 .unsigned_value = src_id,
62306304 });
......@@ -6239,38 +6313,39 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
62396313 });
62406314 result_limbs[1] = cg.allocId();
62416315 try cg.body.emit(gpa, .OpUConvert, .{
6242 .id_result_type = u32_ty_id,
6316 .id_result_type = limb_ty_id,
62436317 .id_result = result_limbs[1],
62446318 .unsigned_value = hi,
62456319 });
62466320 }
62476321 // Sign/zero-extend remaining limbs.
6248 const fill_start: u16 = if (src_info.backing_bits <= 32) 1 else 2;
6322 const fill_start: u16 = if (src_info.backing_bits <= limb_bits) 1 else 2;
62496323 const fill = if (src_info.signedness == .signed) blk: {
6250 const i32_ty_id = try cg.resolveType(.i32, .direct);
6324 const signed_limb_ty: Type = if (limb_bits == 64) .i64 else .i32;
6325 const signed_limb_ty_id = try cg.resolveType(signed_limb_ty, .direct);
62516326 const msb = result_limbs[fill_start - 1];
62526327 const msb_signed = cg.allocId();
62536328 try cg.body.emit(gpa, .OpBitcast, .{
6254 .id_result_type = i32_ty_id,
6329 .id_result_type = signed_limb_ty_id,
62556330 .id_result = msb_signed,
62566331 .operand = msb,
62576332 });
6258 const shift31 = try cg.constInt(.i32, @as(i32, 31));
6333 const shift_amt = try cg.constInt(signed_limb_ty, @as(u64, limb_bits - 1));
62596334 const sign_ext = cg.allocId();
62606335 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
6261 .id_result_type = i32_ty_id,
6336 .id_result_type = signed_limb_ty_id,
62626337 .id_result = sign_ext,
62636338 .base = msb_signed,
6264 .shift = shift31,
6339 .shift = shift_amt,
62656340 });
62666341 const back = cg.allocId();
62676342 try cg.body.emit(gpa, .OpBitcast, .{
6268 .id_result_type = u32_ty_id,
6343 .id_result_type = limb_ty_id,
62696344 .id_result = back,
62706345 .operand = sign_ext,
62716346 });
62726347 break :blk back;
6273 } else try cg.constInt(.u32, @as(u32, 0));
6348 } else try cg.constInt(limb_ty, @as(u64, 0));
62746349 for (fill_start..dst_n) |i| {
62756350 result_limbs[i] = fill;
62766351 }
......@@ -8632,37 +8707,68 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
86328707 const input_ty = cg.typeOf(in.operand);
86338708
86348709 if (std.mem.eql(u8, in.constraint, "c")) {
8635 // constant
8636 const val: Value = .fromInterned(in.operand.toInterned() orelse {
8637 return cg.fail("assembly inputs with 'c' constraint have to be compile-time known", .{});
8638 });
8639
8710 const val: Value = .fromInterned(in.operand.toInterned().?);
86408711 const ip = &zcu.intern_pool;
8641 switch (ip.indexToKey(val.toIntern())) {
8642 .int_type,
8643 .ptr_type,
8644 .array_type,
8645 .vector_type,
8646 .opt_type,
8647 .anyframe_type,
8648 .error_union_type,
8649 .simple_type,
8650 .struct_type,
8651 .union_type,
8652 .opaque_type,
8653 .spirv_type,
8654 .enum_type,
8655 .func_type,
8656 .error_set_type,
8657 .inferred_error_set_type,
8658 => unreachable, // types, not values
8659
8660 .undef => return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}),
8661
8662 .int => try ass.value_map.put(gpa, in.name, .{ .constant = @intCast(val.toUnsignedInt(zcu)) }),
8663 .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }),
8664
8665 else => unreachable, // TODO
8712 const target = cg.pt.zcu.getTarget();
8713 switch (input_ty.zigTypeTag(zcu)) {
8714 .int => {
8715 const bits: u64 = switch (input_ty.intInfo(zcu).signedness) {
8716 .unsigned => val.toUnsignedInt(zcu),
8717 .signed => @bitCast(val.toSignedInt(zcu)),
8718 };
8719 try ass.value_map.put(gpa, in.name, .{ .constant = bits });
8720 },
8721 .float => {
8722 const bits: u64 = switch (input_ty.floatBits(target)) {
8723 16 => @as(u16, @bitCast(val.toFloat(f16, zcu))),
8724 32 => @as(u32, @bitCast(val.toFloat(f32, zcu))),
8725 64 => @bitCast(val.toFloat(f64, zcu)),
8726 else => unreachable, // Sema rejects unsupported float widths.
8727 };
8728 try ass.value_map.put(gpa, in.name, .{ .constant = bits });
8729 },
8730 .vector => {
8731 const child_ty = input_ty.childType(zcu);
8732 const child_kind = child_ty.zigTypeTag(zcu);
8733 const child_bit_width: u16 = switch (child_kind) {
8734 .bool => 0,
8735 .int => @intCast(child_ty.intInfo(zcu).bits),
8736 .float => child_ty.floatBits(target),
8737 else => unreachable, // Sema rejects unsupported vector element types.
8738 };
8739 const vec_len: usize = @intCast(input_ty.vectorLen(zcu));
8740 const values = try gpa.alloc(u64, vec_len);
8741 errdefer gpa.free(values);
8742 for (values, 0..) |*out, i| {
8743 const elem: Value = try val.elemValue(cg.pt, i);
8744 out.* = switch (child_kind) {
8745 .bool => @intFromBool(elem.toBool()),
8746 .int => switch (child_ty.intInfo(zcu).signedness) {
8747 .unsigned => elem.toUnsignedInt(zcu),
8748 .signed => @bitCast(elem.toSignedInt(zcu)),
8749 },
8750 .float => switch (child_bit_width) {
8751 16 => @as(u16, @bitCast(elem.toFloat(f16, zcu))),
8752 32 => @as(u32, @bitCast(elem.toFloat(f32, zcu))),
8753 64 => @bitCast(elem.toFloat(f64, zcu)),
8754 else => unreachable,
8755 },
8756 else => unreachable,
8757 };
8758 }
8759 const child_ty_id = try cg.resolveType(child_ty, .direct);
8760 try ass.value_map.put(gpa, in.name, .{ .constant_composite = .{
8761 .child = child_ty_id,
8762 .child_kind = child_kind,
8763 .child_bit_width = child_bit_width,
8764 .values = values,
8765 } });
8766 },
8767 .@"enum" => switch (ip.indexToKey(val.toIntern())) {
8768 .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }),
8769 else => unreachable,
8770 },
8771 else => unreachable, // Sema rejects unsupported types.
86668772 }
86678773 } else if (std.mem.eql(u8, in.constraint, "t")) {
86688774 // type
......@@ -8729,7 +8835,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
87298835 .just_declared, .unresolved_forward_reference => unreachable,
87308836 .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}),
87318837 .value => |ref| return ref,
8732 .constant, .string => return cg.fail("cannot return constant from assembly", .{}),
8838 .constant, .constant_composite, .string => return cg.fail("cannot return constant from assembly", .{}),
87338839 }
87348840 // TODO: Multiple results
87358841 // TODO: Check that the output type from assembly is the same as the type actually expected by Zig.
......@@ -8749,8 +8855,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
87498855 const callee_ty = cg.typeOf(air_call.callee);
87508856 const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) {
87518857 .@"fn" => callee_ty,
8752 .pointer => return cg.fail("cannot call function pointers", .{}),
8753 else => unreachable,
8858 else => unreachable, // rejected by Sema for SPIR-V
87548859 };
87558860 const fn_info = zcu.typeToFunc(zig_fn_ty).?;
87568861 const return_type = fn_info.return_type;
src/link/SpirV.zig+6-3
......@@ -621,9 +621,12 @@ fn emitPreamble(
621621 },
622622 else => unreachable,
623623 }
624 if (target.os.tag == .vulkan and target.cpu.arch == .spirv64) {
625 caps.insert(.physical_storage_buffer_addresses);
626 try exts.put(gpa, "SPV_KHR_physical_storage_buffer", {});
624 if (target.cpu.arch == .spirv64) {
625 caps.insert(.int64);
626 if (target.os.tag == .vulkan) {
627 caps.insert(.physical_storage_buffer_addresses);
628 try exts.put(gpa, "SPV_KHR_physical_storage_buffer", {});
629 }
627630 }
628631 if (has_linkage) caps.insert(.linkage);
629632
src/link/SpirV/BinaryModule.zig+1-1
......@@ -303,7 +303,7 @@ pub const Parser = struct {
303303 }
304304 },
305305 .literal_context_dependent_number => {
306 assert(inst.opcode == .OpConstant or inst.opcode == .OpSpecConstantOp);
306 assert(inst.opcode == .OpConstant or inst.opcode == .OpSpecConstant);
307307 const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse {
308308 log.err("invalid LiteralContextDependentNumber type {}", .{inst.operands[0]});
309309 return error.InvalidId;
test/behavior/spirv.zig+9
......@@ -49,6 +49,15 @@ test "@SpirvType" {
4949 _ = runtime_array;
5050}
5151
52const InnerStruct = extern struct { x: u32 };
53const OuterStruct = extern struct { inner: InnerStruct, y: u32 };
54const outer_pc = @extern(*addrspace(.push_constant) const OuterStruct, .{ .name = "outer_pc" });
55
56test "@ptrCast to first field type" {
57 const pc_inner: *addrspace(.push_constant) const InnerStruct = @ptrCast(outer_pc);
58 _ = pc_inner;
59}
60
5261test "@SpirvType equality" {
5362 try expect(@SpirvType(.sampler) == Sampler);
5463 try expect(@SpirvType(.{ .runtime_array = u32 }) == RuntimeArray);
test/cases/compile_errors/loading_spirv_runtime_array_value.zig+2-2
......@@ -6,11 +6,11 @@ const buf = @extern(*addrspace(.storage_buffer) Buffer, .{
66 .name = "buf",
77 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
88});
9export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void {
9export fn main() callconv(.kernel) void {
1010 const a = buf.data;
1111 _ = a;
1212}
13export fn main2() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void {
13export fn main2() callconv(.kernel) void {
1414 const p: *addrspace(.storage_buffer) const RuntimeArray = &buf.data;
1515 _ = p.*;
1616}
test/cases/compile_errors/spirv_c_constraint_errors.zig created+33
......@@ -0,0 +1,33 @@
1export fn not_comptime() callconv(.kernel) void {
2 var runtime: u32 = 42;
3 _ = &runtime;
4 _ = asm ("%ret = OpSpecConstant %ty $default"
5 : [ret] "" (-> u32),
6 : [ty] "t" (u32),
7 [default] "c" (runtime),
8 );
9}
10
11export fn undef_input() callconv(.kernel) void {
12 const x: u32 = undefined;
13 _ = asm ("%ret = OpDummy $x"
14 : [ret] "" (-> u32),
15 : [x] "c" (x),
16 );
17}
18
19export fn unsupported_type() callconv(.kernel) void {
20 const s = "hi";
21 _ = asm ("%ret = OpDummy $x"
22 : [ret] "" (-> u32),
23 : [x] "c" (s),
24 );
25}
26
27// error
28// backend=selfhosted
29// target=spirv32-vulkan
30//
31// :7:26: error: assembly input with 'c' constraint must be compile-time known
32// :15:20: error: assembly input with 'c' constraint cannot be undefined
33// :23:20: error: unsupported type '*const [2:0]u8' for 'c' constraint
test/cases/compile_errors/spirv_cannot_call_function_pointer.zig created+13
......@@ -0,0 +1,13 @@
1fn foo() void {}
2
3export fn main() callconv(.kernel) void {
4 var fp = &foo;
5 fp = &foo;
6 fp();
7}
8
9// error
10// backend=selfhosted
11// target=spirv32-vulkan
12//
13// :6:5: error: SPIR-V does not support calling function pointers
test/cases/compile_errors/spirv_extern_var_addrspace.zig created+11
......@@ -0,0 +1,11 @@
1extern var x: u32;
2
3export fn main() callconv(.kernel) void {
4 _ = x;
5}
6
7// error
8// backend=selfhosted
9// target=spirv64-vulkan
10//
11// :1:15: error: SPIR-V extern variables require an explicit address space
test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig created+20
......@@ -0,0 +1,20 @@
1const A = extern struct { x: u32, y: u32 };
2const B = extern struct { a: u64 };
3
4const a = @extern(*addrspace(.uniform) const A, .{
5 .name = "a",
6 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
7});
8
9export fn main() callconv(.kernel) void {
10 const b: *addrspace(.uniform) const B = @ptrCast(a);
11 _ = &b;
12}
13
14// error
15// backend=selfhosted
16// target=spirv32-vulkan
17//
18// :10:44: error: cannot cast pointer '*addrspace(.uniform) const A' to '*addrspace(.uniform) const B'
19// :10:44: note: 'B' must appear at offset 0 inside 'A'
20// :10:44: note: 'uniform' pointers can only reach nested types through a first struct field or an array element
test/cases/compile_errors/spirv_unsupported_float_width.zig created+16
......@@ -0,0 +1,16 @@
1export fn use_f80() callconv(.kernel) void {
2 var x: f80 = 1.5;
3 _ = &x;
4}
5
6export fn use_f16() callconv(.kernel) void {
7 var x: f16 = 1.5;
8 _ = &x;
9}
10
11// error
12// backend=selfhosted
13// target=spirv32-vulkan
14//
15// :2:5: error: 'f80' is not supported on the current SPIR-V feature set
16// :7:5: error: 'f16' is not supported on the current SPIR-V feature set