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(...@@ -2334,7 +2334,8 @@ pub fn supportsAddressSpace(
2334 .constant => (is_gpu and (context == null or context == .constant)) or2334 .constant => (is_gpu and (context == null or context == .constant)) or
2335 (is_spirv and (context == null or context == .constant or context == .pointer)),2335 (is_spirv and (context == null or context == .constant or context == .pointer)),
2336 .param => is_nvptx,2336 .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,
2338 .externref, .funcref => target.cpu.has(.wasm, .reference_types),2339 .externref, .funcref => target.cpu.has(.wasm, .reference_types),
2339 };2340 };
2340}2341}
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/Sema.zig+66-5
...@@ -6917,6 +6917,9 @@ fn analyzeCall(...@@ -6917,6 +6917,9 @@ fn analyzeCall(
6917 const is_inline_call = block.isComptime() or inline_requested;6917 const is_inline_call = block.isComptime() or inline_requested;
69186918
6919 if (!is_inline_call) {6919 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 }
6920 if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: {6923 if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: {
6921 const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)});6924 const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)});
6922 errdefer msg.destroy(gpa);6925 errdefer msg.destroy(gpa);
...@@ -15250,6 +15253,23 @@ fn zirAsm(...@@ -15250,6 +15253,23 @@ fn zirAsm(
15250 }15253 }
1525115254
15252 const constraint = sema.code.nullTerminatedString(input.data.constraint);15255 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 }
15253 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;15273 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
15254 inputs[arg_i] = .{ .c = constraint, .n = name };15274 inputs[arg_i] = .{ .c = constraint, .n = name };
15255 }15275 }
...@@ -21915,6 +21935,34 @@ fn ptrCastFull(...@@ -21915,6 +21935,34 @@ fn ptrCastFull(
2191521935
21916 try sema.validateRuntimeValue(block, operand_src, operand);21936 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
21918 const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));21966 const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));
21919 const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);21967 const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);
21920 const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align);21968 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(...@@ -34488,11 +34536,24 @@ pub fn resolveNavPtrModifiers(
34488 },34536 },
34489 };34537 };
34490 const target = zcu.getTarget();34538 const target = zcu.getTarget();
34491 const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {34539 const addrspace_body = zir_decl.addrspace_body orelse {
34492 .function => target_util.defaultAddressSpace(target, .function),34540 if (zir_decl.linkage == .@"extern" and
34493 .variable => target_util.defaultAddressSpace(target, .global_mutable),34541 target.cpu.arch.isSpirV() and
34494 .constant => target_util.defaultAddressSpace(target, .global_constant),34542 nav_ty.zigTypeTag(zcu) != .@"fn")
34495 else => unreachable,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 };
34496 };34557 };
34497 const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst);34558 const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst);
34498 break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);34559 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) {...@@ -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+328-223
...@@ -37,6 +37,10 @@ prologue: Section = .{},...@@ -37,6 +37,10 @@ prologue: Section = .{},
37body: Section = .{},37body: Section = .{},
38args: std.ArrayList(Id) = .empty,38args: std.ArrayList(Id) = .empty,
39next_arg_index: u32 = 0,39next_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,
40block_stack: std.ArrayList(*Block) = .empty,44block_stack: std.ArrayList(*Block) = .empty,
41block_label: Id = .none,45block_label: Id = .none,
42/// Whether the current block has been terminated by a terminator46/// Whether the current block has been terminated by a terminator
...@@ -49,7 +53,21 @@ tracked_allocas: std.AutoHashMapUnmanaged(Id, ?Id) = .empty,...@@ -49,7 +53,21 @@ tracked_allocas: std.AutoHashMapUnmanaged(Id, ?Id) = .empty,
49loop_switches: std.AutoHashMapUnmanaged(Air.Inst.Index, LoopSwitch) = .empty,53loop_switches: std.AutoHashMapUnmanaged(Air.Inst.Index, LoopSwitch) = .empty,
50id_scratch: std.ArrayList(Id) = .empty,54id_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
54/// Data can be lowered into in two basic representations: indirect, which is when72/// Data can be lowered into in two basic representations: indirect, which is when
55/// a type is stored in memory, and direct, which is how a type is stored when its73/// 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 {...@@ -163,6 +181,7 @@ pub fn deinit(cg: *CodeGen) void {
163 cg.block_stack.deinit(gpa);181 cg.block_stack.deinit(gpa);
164 cg.block_results.deinit(gpa);182 cg.block_results.deinit(gpa);
165 cg.args.deinit(gpa);183 cg.args.deinit(gpa);
184 cg.composite_limbs.deinit(gpa);
166 cg.tracked_allocas.deinit(gpa);185 cg.tracked_allocas.deinit(gpa);
167 cg.inst_results.deinit(gpa);186 cg.inst_results.deinit(gpa);
168 cg.loop_switches.deinit(gpa);187 cg.loop_switches.deinit(gpa);
...@@ -407,7 +426,7 @@ pub fn addEntryPointDeps(...@@ -407,7 +426,7 @@ pub fn addEntryPointDeps(
407 cg: *CodeGen,426 cg: *CodeGen,
408 decl_index: Decl.Index,427 decl_index: Decl.Index,
409 seen: *std.bit_set.Dynamic,428 seen: *std.bit_set.Dynamic,
410 interface: *std.array_list.Managed(Id),429 interface: *std.ArrayList(Id),
411) !void {430) !void {
412 const decl = cg.declPtr(decl_index);431 const decl = cg.declPtr(decl_index);
413 const deps = cg.decl_deps.items[decl.begin_dep..decl.end_dep];432 const deps = cg.decl_deps.items[decl.begin_dep..decl.end_dep];
...@@ -419,7 +438,7 @@ pub fn addEntryPointDeps(...@@ -419,7 +438,7 @@ pub fn addEntryPointDeps(
419 seen.set(@intFromEnum(decl_index));438 seen.set(@intFromEnum(decl_index));
420439
421 if (decl.kind == .global) {440 if (decl.kind == .global) {
422 try interface.append(decl.result_id);441 try interface.append(cg.gpa, decl.result_id);
423 }442 }
424443
425 for (deps) |dep| {444 for (deps) |dep| {
...@@ -471,14 +490,14 @@ pub fn backingIntBits(cg: *const CodeGen, bits: u16) struct { u16, bool } {...@@ -471,14 +490,14 @@ pub fn backingIntBits(cg: *const CodeGen, bits: u16) struct { u16, bool } {
471 .{ .bits = 8, .enabled = target.cpu.has(.spirv, .int8) },490 .{ .bits = 8, .enabled = target.cpu.has(.spirv, .int8) },
472 .{ .bits = 16, .enabled = target.cpu.has(.spirv, .int16) },491 .{ .bits = 16, .enabled = target.cpu.has(.spirv, .int16) },
473 .{ .bits = 32, .enabled = true },492 .{ .bits = 32, .enabled = true },
474 .{ .bits = 64, .enabled = target.cpu.has(.spirv, .int64) or target.cpu.arch == .spirv64 },493 .{ .bits = 64, .enabled = hasInt64(target) },
475 };494 };
476495
477 for (ints) |int| {496 for (ints) |int| {
478 if (bits <= int.bits and int.enabled) return .{ int.bits, false };497 if (bits <= int.bits and int.enabled) return .{ int.bits, false };
479 }498 }
480499
481 return .{ std.mem.alignForward(u16, bits, big_int_bits), true };500 return .{ std.mem.alignForward(u16, bits, cg.bigIntBits()), true };
482}501}
483502
484pub fn intType(cg: *CodeGen, signedness: std.lang.Signedness, bits: u16) !Id {503pub 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 {...@@ -492,14 +511,16 @@ pub fn intType(cg: *CodeGen, signedness: std.lang.Signedness, bits: u16) !Id {
492 };511 };
493 const backing_bits, const big_int = cg.backingIntBits(bits);512 const backing_bits, const big_int = cg.backingIntBits(bits);
494 if (big_int) {513 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);
496 const len_id = cg.allocId();517 const len_id = cg.allocId();
497 try cg.sections.globals.emit(cg.gpa, .OpConstant, .{518 try cg.sections.globals.emit(cg.gpa, .OpConstant, .{
498 .id_result_type = u32_ty,519 .id_result_type = len_ty,
499 .id_result = len_id,520 .id_result = len_id,
500 .value = .{ .uint32 = backing_bits / big_int_bits },521 .value = .{ .uint32 = backing_bits / limb_bits },
501 });522 });
502 return cg.arrayType(len_id, u32_ty);523 return cg.arrayType(len_id, limb_ty);
503 }524 }
504525
505 const result_id = cg.allocId();526 const result_id = cg.allocId();
...@@ -1443,7 +1464,7 @@ fn constInt(cg: *CodeGen, ty: Type, value: anytype) !Id {...@@ -1443,7 +1464,7 @@ fn constInt(cg: *CodeGen, ty: Type, value: anytype) !Id {
1443 .signed => @bitCast(@as(i64, @intCast(value))),1464 .signed => @bitCast(@as(i64, @intCast(value))),
1444 .unsigned => @as(u64, @intCast(value)),1465 .unsigned => @as(u64, @intCast(value)),
1445 };1466 };
1446 const n_limbs = backing_bits / big_int_bits;1467 const n_limbs = backing_bits / cg.bigIntBits();
1447 const fill: u32 = if (signedness == .signed and value < 0) 0xFFFFFFFF else 0;1468 const fill: u32 = if (signedness == .signed and value < 0) 0xFFFFFFFF else 0;
1448 const scratch_top = cg.id_scratch.items.len;1469 const scratch_top = cg.id_scratch.items.len;
1449 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);1470 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
...@@ -1616,21 +1637,33 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {...@@ -1616,21 +1637,33 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
1616 const int_info = ty.intInfo(zcu);1637 const int_info = ty.intInfo(zcu);
1617 const backing_bits, const is_big_int = cg.backingIntBits(int_info.bits);1638 const backing_bits, const is_big_int = cg.backingIntBits(int_info.bits);
1618 if (is_big_int) {1639 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;
1620 const big_result_ty_id = try cg.resolveType(ty, .indirect);1642 const big_result_ty_id = try cg.resolveType(ty, .indirect);
1621 var bigint_space: Value.BigIntSpace = undefined;1643 var bigint_space: Value.BigIntSpace = undefined;
1622 const bigint = val.toBigInt(&bigint_space, zcu);1644 const bigint = val.toBigInt(&bigint_space, zcu);
1623 const limb_values = try gpa.alloc(u32, n_limbs);1645 const limb_bytes = try gpa.alloc(u8, backing_bits / 8);
1624 defer gpa.free(limb_values);1646 defer gpa.free(limb_bytes);
1625 bigint.writeTwosComplement(std.mem.sliceAsBytes(limb_values), .little);1647 bigint.writeTwosComplement(limb_bytes, .little);
1626 if (builtin.cpu.arch.endian() == .big) {
1627 for (limb_values) |*limb| limb.* = @byteSwap(limb.*);
1628 }
1629 const scratch_top = cg.id_scratch.items.len;1648 const scratch_top = cg.id_scratch.items.len;
1630 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);1649 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
1631 const constituents = try cg.id_scratch.addManyAsSlice(gpa, n_limbs);1650 const constituents = try cg.id_scratch.addManyAsSlice(gpa, n_limbs);
1632 for (constituents, 0..) |*c, i| {1651 switch (limb_bits) {
1633 c.* = try cg.constInt(.u32, limb_values[i]);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,
1634 }1667 }
1635 break :cache try cg.constructComposite(big_result_ty_id, constituents);1668 break :cache try cg.constructComposite(big_result_ty_id, constituents);
1636 }1669 }
...@@ -1776,11 +1809,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {...@@ -1776,11 +1809,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
1776 const struct_type = zcu.typeToStruct(ty).?;1809 const struct_type = zcu.typeToStruct(ty).?;
1777 assert(struct_type.layout != .@"packed"); // packed structs use `bitpack`1810 assert(struct_type.layout != .@"packed"); // packed structs use `bitpack`
17781811
1779 var types = std.array_list.Managed(Type).init(gpa);1812 var types: std.ArrayList(Type) = .empty;
1780 defer types.deinit();1813 defer types.deinit(gpa);
17811814
1782 var constituents = std.array_list.Managed(Id).init(gpa);1815 var constituents: std.ArrayList(Id) = .empty;
1783 defer constituents.deinit();1816 defer constituents.deinit(gpa);
17841817
1785 var it = struct_type.iterateRuntimeOrder(ip);1818 var it = struct_type.iterateRuntimeOrder(ip);
1786 while (it.next()) |field_index| {1819 while (it.next()) |field_index| {
...@@ -1794,8 +1827,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {...@@ -1794,8 +1827,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
1794 const field_val = try val.fieldValue(pt, field_index);1827 const field_val = try val.fieldValue(pt, field_index);
1795 const field_id = try cg.constant(field_ty, field_val, .indirect);1828 const field_id = try cg.constant(field_ty, field_val, .indirect);
17961829
1797 try types.append(field_ty);1830 try types.append(gpa, field_ty);
1798 try constituents.append(field_id);1831 try constituents.append(gpa, field_id);
1799 }1832 }
18001833
1801 const comp_ty_id = try cg.resolveType(ty, .direct);1834 const comp_ty_id = try cg.resolveType(ty, .direct);
...@@ -2189,14 +2222,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -2189,14 +2222,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2189 64 => target.cpu.has(.spirv, .float64),2222 64 => target.cpu.has(.spirv, .float64),
2190 else => false,2223 else => false,
2191 };2224 };
21922225 if (!supported) return cg.fail(
2193 if (!supported) {2226 "'{f}' is not supported on the current SPIR-V feature set",
2194 return cg.fail(2227 .{ty.fmt(cg.pt)},
2195 "floating point width of {} bits is not supported for the current SPIR-V feature set",2228 );
2196 .{bits},
2197 );
2198 }
2199
2200 return try cg.floatType(bits);2229 return try cg.floatType(bits);
2201 },2230 },
2202 .array => {2231 .array => {
...@@ -2288,7 +2317,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -2288,7 +2317,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2288 }),2317 }),
2289 },2318 },
2290 };2319 };
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);
2292 const storage_class = cg.storageClass(ptr_info.flags.address_space);2321 const storage_class = cg.storageClass(ptr_info.flags.address_space);
2293 const ptr_ty_id = try cg.ptrType(child_ty_id, storage_class);2322 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 {...@@ -2336,11 +2365,11 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2336 return try cg.resolveType(.fromInterned(struct_type.packed_backing_int_type), .direct);2365 return try cg.resolveType(.fromInterned(struct_type.packed_backing_int_type), .direct);
2337 }2366 }
23382367
2339 var member_types = std.array_list.Managed(Id).init(gpa);2368 var member_types: std.ArrayList(Id) = .empty;
2340 defer member_types.deinit();2369 defer member_types.deinit(gpa);
23412370
2342 var member_names = std.array_list.Managed([]const u8).init(gpa);2371 var member_names: std.ArrayList([]const u8) = .empty;
2343 defer member_names.deinit();2372 defer member_names.deinit(gpa);
23442373
2345 var it = struct_type.iterateRuntimeOrder(ip);2374 var it = struct_type.iterateRuntimeOrder(ip);
2346 while (it.next()) |field_index| {2375 while (it.next()) |field_index| {
...@@ -2348,8 +2377,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -2348,8 +2377,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2348 if (!field_ty.hasRuntimeBits(zcu)) continue;2377 if (!field_ty.hasRuntimeBits(zcu)) continue;
23492378
2350 const field_name = struct_type.field_names.get(ip)[field_index];2379 const field_name = struct_type.field_names.get(ip)[field_index];
2351 try member_types.append(try cg.resolveType(field_ty, .indirect));2380 try member_types.append(gpa, try cg.resolveType(field_ty, .indirect));
2352 try member_names.append(field_name.toSlice(ip));2381 try member_names.append(gpa, field_name.toSlice(ip));
2353 }2382 }
23542383
2355 const result_id = try cg.structType(2384 const result_id = try cg.structType(
...@@ -2766,20 +2795,28 @@ const CompositeInt = struct {...@@ -2766,20 +2795,28 @@ const CompositeInt = struct {
2766 info: ArithmeticTypeInfo,2795 info: ArithmeticTypeInfo,
27672796
2768 fn init(cg: *CodeGen, composite_id: Id, info: ArithmeticTypeInfo) !CompositeInt {2797 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();
2770 const gpa = cg.gpa;2799 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();
2772 const limbs = try cg.id_scratch.addManyAsSlice(gpa, n_limbs);2807 const limbs = try cg.id_scratch.addManyAsSlice(gpa, n_limbs);
2773 for (limbs, 0..) |*limb, i| {2808 for (limbs, 0..) |*limb, i| {
2774 const result_id = cg.allocId();2809 const result_id = cg.allocId();
2775 try cg.body.emit(gpa, .OpCompositeExtract, .{2810 try cg.body.emit(gpa, .OpCompositeExtract, .{
2776 .id_result_type = u32_ty_id,2811 .id_result_type = limb_ty_id,
2777 .id_result = result_id,2812 .id_result = result_id,
2778 .composite = composite_id,2813 .composite = composite_id,
2779 .indexes = &.{@as(u32, @intCast(i))},2814 .indexes = &.{@as(u32, @intCast(i))},
2780 });2815 });
2781 limb.* = result_id;2816 limb.* = result_id;
2782 }2817 }
2818 const cached = try cg.arena.dupe(Id, limbs);
2819 try cg.composite_limbs.put(gpa, composite_id, cached);
2783 return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info };2820 return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info };
2784 }2821 }
27852822
...@@ -2793,9 +2830,9 @@ const CompositeInt = struct {...@@ -2793,9 +2830,9 @@ const CompositeInt = struct {
2793 }2830 }
27942831
2795 fn zero(cg: *CodeGen, info: ArithmeticTypeInfo) !CompositeInt {2832 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();
2797 const limbs = try cg.id_scratch.addManyAsSlice(cg.gpa, n_limbs);2834 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));
2799 for (limbs) |*limb| limb.* = zero_id;2836 for (limbs) |*limb| limb.* = zero_id;
2800 return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info };2837 return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info };
2801 }2838 }
...@@ -2808,10 +2845,10 @@ const CompositeInt = struct {...@@ -2808,10 +2845,10 @@ const CompositeInt = struct {
2808 fn limbBinOp(ci: CompositeInt, opcode: Opcode, lhs: Id, rhs: Id) !Id {2845 fn limbBinOp(ci: CompositeInt, opcode: Opcode, lhs: Id, rhs: Id) !Id {
2809 const cg = ci.cg;2846 const cg = ci.cg;
2810 const gpa = cg.gpa;2847 const gpa = cg.gpa;
2811 const u32_ty_id = try cg.resolveType(.u32, .direct);2848 const limb_ty_id = try cg.limbTypeId();
2812 const result_id = cg.allocId();2849 const result_id = cg.allocId();
2813 try cg.body.emitRaw(gpa, opcode, 4);2850 try cg.body.emitRaw(gpa, opcode, 4);
2814 cg.body.writeOperand(Id, u32_ty_id);2851 cg.body.writeOperand(Id, limb_ty_id);
2815 cg.body.writeOperand(Id, result_id);2852 cg.body.writeOperand(Id, result_id);
2816 cg.body.writeOperand(Id, lhs);2853 cg.body.writeOperand(Id, lhs);
2817 cg.body.writeOperand(Id, rhs);2854 cg.body.writeOperand(Id, rhs);
...@@ -2821,10 +2858,10 @@ const CompositeInt = struct {...@@ -2821,10 +2858,10 @@ const CompositeInt = struct {
2821 fn limbUnOp(ci: CompositeInt, opcode: Opcode, operand: Id) !Id {2858 fn limbUnOp(ci: CompositeInt, opcode: Opcode, operand: Id) !Id {
2822 const cg = ci.cg;2859 const cg = ci.cg;
2823 const gpa = cg.gpa;2860 const gpa = cg.gpa;
2824 const u32_ty_id = try cg.resolveType(.u32, .direct);2861 const limb_ty_id = try cg.limbTypeId();
2825 const result_id = cg.allocId();2862 const result_id = cg.allocId();
2826 try cg.body.emitRaw(gpa, opcode, 3);2863 try cg.body.emitRaw(gpa, opcode, 3);
2827 cg.body.writeOperand(Id, u32_ty_id);2864 cg.body.writeOperand(Id, limb_ty_id);
2828 cg.body.writeOperand(Id, result_id);2865 cg.body.writeOperand(Id, result_id);
2829 cg.body.writeOperand(Id, operand);2866 cg.body.writeOperand(Id, operand);
2830 return result_id;2867 return result_id;
...@@ -2916,16 +2953,17 @@ const CompositeInt = struct {...@@ -2916,16 +2953,17 @@ const CompositeInt = struct {
2916 var cmp_l = l;2953 var cmp_l = l;
2917 var cmp_r = r;2954 var cmp_r = r;
2918 if (use_signed) {2955 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);
2920 const sl = cg.allocId();2958 const sl = cg.allocId();
2921 try cg.body.emit(gpa, .OpBitcast, .{2959 try cg.body.emit(gpa, .OpBitcast, .{
2922 .id_result_type = i32_ty_id,2960 .id_result_type = signed_limb_ty_id,
2923 .id_result = sl,2961 .id_result = sl,
2924 .operand = l,2962 .operand = l,
2925 });2963 });
2926 const sr = cg.allocId();2964 const sr = cg.allocId();
2927 try cg.body.emit(gpa, .OpBitcast, .{2965 try cg.body.emit(gpa, .OpBitcast, .{
2928 .id_result_type = i32_ty_id,2966 .id_result_type = signed_limb_ty_id,
2929 .id_result = sr,2967 .id_result = sr,
2930 .operand = r,2968 .operand = r,
2931 });2969 });
...@@ -2969,16 +3007,17 @@ const CompositeInt = struct {...@@ -2969,16 +3007,17 @@ const CompositeInt = struct {
2969 const comp = zcu.comp;3007 const comp = zcu.comp;
2970 const io = comp.io;3008 const io = comp.io;
29713009
2972 const u32_zig = try pt.intType(.unsigned, 32);3010 const limb_bits = cg.bigIntBits();
2973 const u32_ty_id = try cg.resolveType(.u32, .direct);3011 const limb_zig = try pt.intType(.unsigned, limb_bits);
3012 const limb_ty_id = try cg.limbTypeId();
2974 const carry_struct_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{3013 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() },
2976 .values = &.{ .none, .none },3015 .values = &.{ .none, .none },
2977 }));3016 }));
2978 const carry_struct_ty_id = try cg.resolveType(carry_struct_ty, .direct);3017 const carry_struct_ty_id = try cg.resolveType(carry_struct_ty, .direct);
29793018
2980 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs);3019 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
2983 const opcode: Opcode = if (is_add) .OpIAddCarry else .OpISubBorrow;3022 const opcode: Opcode = if (is_add) .OpIAddCarry else .OpISubBorrow;
29843023
...@@ -2992,14 +3031,14 @@ const CompositeInt = struct {...@@ -2992,14 +3031,14 @@ const CompositeInt = struct {
29923031
2993 const sum1 = cg.allocId();3032 const sum1 = cg.allocId();
2994 try cg.body.emit(gpa, .OpCompositeExtract, .{3033 try cg.body.emit(gpa, .OpCompositeExtract, .{
2995 .id_result_type = u32_ty_id,3034 .id_result_type = limb_ty_id,
2996 .id_result = sum1,3035 .id_result = sum1,
2997 .composite = op1,3036 .composite = op1,
2998 .indexes = &.{0},3037 .indexes = &.{0},
2999 });3038 });
3000 const carry1 = cg.allocId();3039 const carry1 = cg.allocId();
3001 try cg.body.emit(gpa, .OpCompositeExtract, .{3040 try cg.body.emit(gpa, .OpCompositeExtract, .{
3002 .id_result_type = u32_ty_id,3041 .id_result_type = limb_ty_id,
3003 .id_result = carry1,3042 .id_result = carry1,
3004 .composite = op1,3043 .composite = op1,
3005 .indexes = &.{1},3044 .indexes = &.{1},
...@@ -3014,14 +3053,14 @@ const CompositeInt = struct {...@@ -3014,14 +3053,14 @@ const CompositeInt = struct {
30143053
3015 result_limbs[i] = cg.allocId();3054 result_limbs[i] = cg.allocId();
3016 try cg.body.emit(gpa, .OpCompositeExtract, .{3055 try cg.body.emit(gpa, .OpCompositeExtract, .{
3017 .id_result_type = u32_ty_id,3056 .id_result_type = limb_ty_id,
3018 .id_result = result_limbs[i],3057 .id_result = result_limbs[i],
3019 .composite = op2,3058 .composite = op2,
3020 .indexes = &.{0},3059 .indexes = &.{0},
3021 });3060 });
3022 const carry2 = cg.allocId();3061 const carry2 = cg.allocId();
3023 try cg.body.emit(gpa, .OpCompositeExtract, .{3062 try cg.body.emit(gpa, .OpCompositeExtract, .{
3024 .id_result_type = u32_ty_id,3063 .id_result_type = limb_ty_id,
3025 .id_result = carry2,3064 .id_result = carry2,
3026 .composite = op2,3065 .composite = op2,
3027 .indexes = &.{1},3066 .indexes = &.{1},
...@@ -3036,16 +3075,18 @@ const CompositeInt = struct {...@@ -3036,16 +3075,18 @@ const CompositeInt = struct {
3036 fn shl(ci: CompositeInt, shift_amt_id: Id) !CompositeInt {3075 fn shl(ci: CompositeInt, shift_amt_id: Id) !CompositeInt {
3037 const cg = ci.cg;3076 const cg = ci.cg;
3038 const gpa = cg.gpa;3077 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();
3040 const bool_ty_id = try cg.resolveType(.bool, .direct);3081 const bool_ty_id = try cg.resolveType(.bool, .direct);
3041 const zero_id = try cg.constInt(.u32, @as(u32, 0));3082 const zero_id = try cg.constInt(limb_ty, @as(u64, 0));
3042 const five_id = try cg.constInt(.u32, @as(u32, 5));3083 const log2_bits_id = try cg.constInt(limb_ty, @as(u64, std.math.log2_int(u16, limb_bits)));
3043 const thirty_one_id = try cg.constInt(.u32, @as(u32, 31));3084 const bits_minus_1_id = try cg.constInt(limb_ty, @as(u64, limb_bits - 1));
3044 const thirty_two_id = try cg.constInt(.u32, @as(u32, 32));3085 const bits_id = try cg.constInt(limb_ty, @as(u64, limb_bits));
30453086
3046 const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, five_id);3087 const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, log2_bits_id);
3047 const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, thirty_one_id);3088 const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, bits_minus_1_id);
3048 const comp_frac = try ci.limbBinOp(.OpISub, thirty_two_id, frac);3089 const comp_frac = try ci.limbBinOp(.OpISub, bits_id, frac);
3049 const frac_is_zero = blk: {3090 const frac_is_zero = blk: {
3050 const r = cg.allocId();3091 const r = cg.allocId();
3051 try cg.body.emit(gpa, .OpIEqual, .{3092 try cg.body.emit(gpa, .OpIEqual, .{
...@@ -3060,12 +3101,12 @@ const CompositeInt = struct {...@@ -3060,12 +3101,12 @@ const CompositeInt = struct {
3060 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs);3101 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs);
30613102
3062 for (0..ci.n_limbs) |i| {3103 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)));
3064 var main_val = zero_id;3105 var main_val = zero_id;
3065 var carry_val = zero_id;3106 var carry_val = zero_id;
30663107
3067 for (0..ci.n_limbs) |j| {3108 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)));
3069 const j_plus_whole = try ci.limbBinOp(.OpIAdd, j_id, whole);3110 const j_plus_whole = try ci.limbBinOp(.OpIAdd, j_id, whole);
30703111
3071 const is_main = blk: {3112 const is_main = blk: {
...@@ -3082,7 +3123,7 @@ const CompositeInt = struct {...@@ -3082,7 +3123,7 @@ const CompositeInt = struct {
3082 main_val = blk: {3123 main_val = blk: {
3083 const r = cg.allocId();3124 const r = cg.allocId();
3084 try cg.body.emit(gpa, .OpSelect, .{3125 try cg.body.emit(gpa, .OpSelect, .{
3085 .id_result_type = u32_ty_id,3126 .id_result_type = limb_ty_id,
3086 .id_result = r,3127 .id_result = r,
3087 .condition = is_main,3128 .condition = is_main,
3088 .object_1 = shifted,3129 .object_1 = shifted,
...@@ -3091,7 +3132,7 @@ const CompositeInt = struct {...@@ -3091,7 +3132,7 @@ const CompositeInt = struct {
3091 break :blk r;3132 break :blk r;
3092 };3133 };
30933134
3094 const one_id = try cg.constInt(.u32, @as(u32, 1));3135 const one_id = try cg.constInt(limb_ty, @as(u64, 1));
3095 const j_plus_whole_plus_1 = try ci.limbBinOp(.OpIAdd, j_plus_whole, one_id);3136 const j_plus_whole_plus_1 = try ci.limbBinOp(.OpIAdd, j_plus_whole, one_id);
3096 const is_carry = blk: {3137 const is_carry = blk: {
3097 const r = cg.allocId();3138 const r = cg.allocId();
...@@ -3107,7 +3148,7 @@ const CompositeInt = struct {...@@ -3107,7 +3148,7 @@ const CompositeInt = struct {
3107 const guarded_carry = blk: {3148 const guarded_carry = blk: {
3108 const r = cg.allocId();3149 const r = cg.allocId();
3109 try cg.body.emit(gpa, .OpSelect, .{3150 try cg.body.emit(gpa, .OpSelect, .{
3110 .id_result_type = u32_ty_id,3151 .id_result_type = limb_ty_id,
3111 .id_result = r,3152 .id_result = r,
3112 .condition = frac_is_zero,3153 .condition = frac_is_zero,
3113 .object_1 = zero_id,3154 .object_1 = zero_id,
...@@ -3118,7 +3159,7 @@ const CompositeInt = struct {...@@ -3118,7 +3159,7 @@ const CompositeInt = struct {
3118 carry_val = blk: {3159 carry_val = blk: {
3119 const r = cg.allocId();3160 const r = cg.allocId();
3120 try cg.body.emit(gpa, .OpSelect, .{3161 try cg.body.emit(gpa, .OpSelect, .{
3121 .id_result_type = u32_ty_id,3162 .id_result_type = limb_ty_id,
3122 .id_result = r,3163 .id_result = r,
3123 .condition = is_carry,3164 .condition = is_carry,
3124 .object_1 = guarded_carry,3165 .object_1 = guarded_carry,
...@@ -3137,16 +3178,18 @@ const CompositeInt = struct {...@@ -3137,16 +3178,18 @@ const CompositeInt = struct {
3137 fn shr(ci: CompositeInt, shift_amt_id: Id, comptime is_arithmetic: bool) !CompositeInt {3178 fn shr(ci: CompositeInt, shift_amt_id: Id, comptime is_arithmetic: bool) !CompositeInt {
3138 const cg = ci.cg;3179 const cg = ci.cg;
3139 const gpa = cg.gpa;3180 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();
3141 const bool_ty_id = try cg.resolveType(.bool, .direct);3184 const bool_ty_id = try cg.resolveType(.bool, .direct);
3142 const zero_id = try cg.constInt(.u32, @as(u32, 0));3185 const zero_id = try cg.constInt(limb_ty, @as(u64, 0));
3143 const five_id = try cg.constInt(.u32, @as(u32, 5));3186 const log2_bits_id = try cg.constInt(limb_ty, @as(u64, std.math.log2_int(u16, limb_bits)));
3144 const thirty_one_id = try cg.constInt(.u32, @as(u32, 31));3187 const bits_minus_1_id = try cg.constInt(limb_ty, @as(u64, limb_bits - 1));
3145 const thirty_two_id = try cg.constInt(.u32, @as(u32, 32));3188 const bits_id = try cg.constInt(limb_ty, @as(u64, limb_bits));
31463189
3147 const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, five_id);3190 const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, log2_bits_id);
3148 const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, thirty_one_id);3191 const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, bits_minus_1_id);
3149 const comp_frac = try ci.limbBinOp(.OpISub, thirty_two_id, frac);3192 const comp_frac = try ci.limbBinOp(.OpISub, bits_id, frac);
3150 const frac_is_zero = blk: {3193 const frac_is_zero = blk: {
3151 const r = cg.allocId();3194 const r = cg.allocId();
3152 try cg.body.emit(gpa, .OpIEqual, .{3195 try cg.body.emit(gpa, .OpIEqual, .{
...@@ -3159,24 +3202,25 @@ const CompositeInt = struct {...@@ -3159,24 +3202,25 @@ const CompositeInt = struct {
3159 };3202 };
31603203
3161 const fill_id = if (is_arithmetic) blk: {3204 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);
3163 const msb_signed = cg.allocId();3207 const msb_signed = cg.allocId();
3164 try cg.body.emit(gpa, .OpBitcast, .{3208 try cg.body.emit(gpa, .OpBitcast, .{
3165 .id_result_type = i32_ty_id,3209 .id_result_type = signed_limb_ty_id,
3166 .id_result = msb_signed,3210 .id_result = msb_signed,
3167 .operand = ci.limbs[ci.n_limbs - 1],3211 .operand = ci.limbs[ci.n_limbs - 1],
3168 });3212 });
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));
3170 const sign_ext = cg.allocId();3214 const sign_ext = cg.allocId();
3171 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{3215 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
3172 .id_result_type = i32_ty_id,3216 .id_result_type = signed_limb_ty_id,
3173 .id_result = sign_ext,3217 .id_result = sign_ext,
3174 .base = msb_signed,3218 .base = msb_signed,
3175 .shift = shift31,3219 .shift = shift_amt,
3176 });3220 });
3177 const back = cg.allocId();3221 const back = cg.allocId();
3178 try cg.body.emit(gpa, .OpBitcast, .{3222 try cg.body.emit(gpa, .OpBitcast, .{
3179 .id_result_type = u32_ty_id,3223 .id_result_type = limb_ty_id,
3180 .id_result = back,3224 .id_result = back,
3181 .operand = sign_ext,3225 .operand = sign_ext,
3182 });3226 });
...@@ -3189,7 +3233,7 @@ const CompositeInt = struct {...@@ -3189,7 +3233,7 @@ const CompositeInt = struct {
3189 const shifted_fill = try ci.limbBinOp(.OpShiftLeftLogical, fill_id, comp_frac);3233 const shifted_fill = try ci.limbBinOp(.OpShiftLeftLogical, fill_id, comp_frac);
3190 const guarded = cg.allocId();3234 const guarded = cg.allocId();
3191 try cg.body.emit(gpa, .OpSelect, .{3235 try cg.body.emit(gpa, .OpSelect, .{
3192 .id_result_type = u32_ty_id,3236 .id_result_type = limb_ty_id,
3193 .id_result = guarded,3237 .id_result = guarded,
3194 .condition = frac_is_zero,3238 .condition = frac_is_zero,
3195 .object_1 = zero_id,3239 .object_1 = zero_id,
...@@ -3199,12 +3243,12 @@ const CompositeInt = struct {...@@ -3199,12 +3243,12 @@ const CompositeInt = struct {
3199 } else zero_id;3243 } else zero_id;
32003244
3201 for (0..ci.n_limbs) |i| {3245 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)));
3203 var main_val = fill_id;3247 var main_val = fill_id;
3204 var carry_val = arith_carry_init;3248 var carry_val = arith_carry_init;
32053249
3206 for (0..ci.n_limbs) |j| {3250 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)));
3208 const i_plus_whole = try ci.limbBinOp(.OpIAdd, i_id, whole);3252 const i_plus_whole = try ci.limbBinOp(.OpIAdd, i_id, whole);
3209 const is_main = blk: {3253 const is_main = blk: {
3210 const r = cg.allocId();3254 const r = cg.allocId();
...@@ -3220,7 +3264,7 @@ const CompositeInt = struct {...@@ -3220,7 +3264,7 @@ const CompositeInt = struct {
3220 main_val = blk: {3264 main_val = blk: {
3221 const r = cg.allocId();3265 const r = cg.allocId();
3222 try cg.body.emit(gpa, .OpSelect, .{3266 try cg.body.emit(gpa, .OpSelect, .{
3223 .id_result_type = u32_ty_id,3267 .id_result_type = limb_ty_id,
3224 .id_result = r,3268 .id_result = r,
3225 .condition = is_main,3269 .condition = is_main,
3226 .object_1 = shifted,3270 .object_1 = shifted,
...@@ -3229,7 +3273,7 @@ const CompositeInt = struct {...@@ -3229,7 +3273,7 @@ const CompositeInt = struct {
3229 break :blk r;3273 break :blk r;
3230 };3274 };
32313275
3232 const one_id = try cg.constInt(.u32, @as(u32, 1));3276 const one_id = try cg.constInt(limb_ty, @as(u64, 1));
3233 const i_plus_whole_plus_1 = try ci.limbBinOp(.OpIAdd, i_plus_whole, one_id);3277 const i_plus_whole_plus_1 = try ci.limbBinOp(.OpIAdd, i_plus_whole, one_id);
3234 const is_carry = blk: {3278 const is_carry = blk: {
3235 const r = cg.allocId();3279 const r = cg.allocId();
...@@ -3245,7 +3289,7 @@ const CompositeInt = struct {...@@ -3245,7 +3289,7 @@ const CompositeInt = struct {
3245 const guarded_carry = blk: {3289 const guarded_carry = blk: {
3246 const r = cg.allocId();3290 const r = cg.allocId();
3247 try cg.body.emit(gpa, .OpSelect, .{3291 try cg.body.emit(gpa, .OpSelect, .{
3248 .id_result_type = u32_ty_id,3292 .id_result_type = limb_ty_id,
3249 .id_result = r,3293 .id_result = r,
3250 .condition = frac_is_zero,3294 .condition = frac_is_zero,
3251 .object_1 = zero_id,3295 .object_1 = zero_id,
...@@ -3256,7 +3300,7 @@ const CompositeInt = struct {...@@ -3256,7 +3300,7 @@ const CompositeInt = struct {
3256 carry_val = blk: {3300 carry_val = blk: {
3257 const r = cg.allocId();3301 const r = cg.allocId();
3258 try cg.body.emit(gpa, .OpSelect, .{3302 try cg.body.emit(gpa, .OpSelect, .{
3259 .id_result_type = u32_ty_id,3303 .id_result_type = limb_ty_id,
3260 .id_result = r,3304 .id_result = r,
3261 .condition = is_carry,3305 .condition = is_carry,
3262 .object_1 = guarded_carry,3306 .object_1 = guarded_carry,
...@@ -3284,17 +3328,18 @@ const CompositeInt = struct {...@@ -3284,17 +3328,18 @@ const CompositeInt = struct {
32843328
3285 const n: usize = ci.n_limbs;3329 const n: usize = ci.n_limbs;
3286 const total: usize = if (wide) 2 * n else n;3330 const total: usize = if (wide) 2 * n else n;
3287 const u32_zig = try pt.intType(.unsigned, 32);3331 const limb_bits = cg.bigIntBits();
3288 const u32_ty_id = try cg.resolveType(.u32, .direct);3332 const limb_zig = try pt.intType(.unsigned, limb_bits);
3333 const limb_ty_id = try cg.limbTypeId();
32893334
3290 const pair_struct_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{3335 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() },
3292 .values = &.{ .none, .none },3337 .values = &.{ .none, .none },
3293 }));3338 }));
3294 const pair_struct_ty_id = try cg.resolveType(pair_struct_ty, .direct);3339 const pair_struct_ty_id = try cg.resolveType(pair_struct_ty, .direct);
32953340
3296 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, total);3341 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));
3298 for (result_limbs) |*r| r.* = zero_id;3343 for (result_limbs) |*r| r.* = zero_id;
32993344
3300 for (0..n) |i| {3345 for (0..n) |i| {
...@@ -3309,7 +3354,7 @@ const CompositeInt = struct {...@@ -3309,7 +3354,7 @@ const CompositeInt = struct {
3309 .opencl => {3354 .opencl => {
3310 lo = cg.allocId();3355 lo = cg.allocId();
3311 try cg.body.emit(gpa, .OpIMul, .{3356 try cg.body.emit(gpa, .OpIMul, .{
3312 .id_result_type = u32_ty_id,3357 .id_result_type = limb_ty_id,
3313 .id_result = lo,3358 .id_result = lo,
3314 .operand_1 = ci.limbs[i],3359 .operand_1 = ci.limbs[i],
3315 .operand_2 = other.limbs[j],3360 .operand_2 = other.limbs[j],
...@@ -3318,7 +3363,7 @@ const CompositeInt = struct {...@@ -3318,7 +3363,7 @@ const CompositeInt = struct {
3318 const set = try cg.importExtendedSet();3363 const set = try cg.importExtendedSet();
3319 hi = cg.allocId();3364 hi = cg.allocId();
3320 try cg.body.emit(gpa, .OpExtInst, .{3365 try cg.body.emit(gpa, .OpExtInst, .{
3321 .id_result_type = u32_ty_id,3366 .id_result_type = limb_ty_id,
3322 .id_result = hi,3367 .id_result = hi,
3323 .set = set,3368 .set = set,
3324 .instruction = .{ .inst = @intFromEnum(spec.OpenClOpcode.u_mul_hi) },3369 .instruction = .{ .inst = @intFromEnum(spec.OpenClOpcode.u_mul_hi) },
...@@ -3336,14 +3381,14 @@ const CompositeInt = struct {...@@ -3336,14 +3381,14 @@ const CompositeInt = struct {
33363381
3337 lo = cg.allocId();3382 lo = cg.allocId();
3338 try cg.body.emit(gpa, .OpCompositeExtract, .{3383 try cg.body.emit(gpa, .OpCompositeExtract, .{
3339 .id_result_type = u32_ty_id,3384 .id_result_type = limb_ty_id,
3340 .id_result = lo,3385 .id_result = lo,
3341 .composite = mul_result,3386 .composite = mul_result,
3342 .indexes = &.{0},3387 .indexes = &.{0},
3343 });3388 });
3344 hi = cg.allocId();3389 hi = cg.allocId();
3345 try cg.body.emit(gpa, .OpCompositeExtract, .{3390 try cg.body.emit(gpa, .OpCompositeExtract, .{
3346 .id_result_type = u32_ty_id,3391 .id_result_type = limb_ty_id,
3347 .id_result = hi,3392 .id_result = hi,
3348 .composite = mul_result,3393 .composite = mul_result,
3349 .indexes = &.{1},3394 .indexes = &.{1},
...@@ -3361,14 +3406,14 @@ const CompositeInt = struct {...@@ -3361,14 +3406,14 @@ const CompositeInt = struct {
33613406
3362 const sum1 = cg.allocId();3407 const sum1 = cg.allocId();
3363 try cg.body.emit(gpa, .OpCompositeExtract, .{3408 try cg.body.emit(gpa, .OpCompositeExtract, .{
3364 .id_result_type = u32_ty_id,3409 .id_result_type = limb_ty_id,
3365 .id_result = sum1,3410 .id_result = sum1,
3366 .composite = add1,3411 .composite = add1,
3367 .indexes = &.{0},3412 .indexes = &.{0},
3368 });3413 });
3369 const c1 = cg.allocId();3414 const c1 = cg.allocId();
3370 try cg.body.emit(gpa, .OpCompositeExtract, .{3415 try cg.body.emit(gpa, .OpCompositeExtract, .{
3371 .id_result_type = u32_ty_id,3416 .id_result_type = limb_ty_id,
3372 .id_result = c1,3417 .id_result = c1,
3373 .composite = add1,3418 .composite = add1,
3374 .indexes = &.{1},3419 .indexes = &.{1},
...@@ -3384,14 +3429,14 @@ const CompositeInt = struct {...@@ -3384,14 +3429,14 @@ const CompositeInt = struct {
33843429
3385 result_limbs[k] = cg.allocId();3430 result_limbs[k] = cg.allocId();
3386 try cg.body.emit(gpa, .OpCompositeExtract, .{3431 try cg.body.emit(gpa, .OpCompositeExtract, .{
3387 .id_result_type = u32_ty_id,3432 .id_result_type = limb_ty_id,
3388 .id_result = result_limbs[k],3433 .id_result = result_limbs[k],
3389 .composite = add2,3434 .composite = add2,
3390 .indexes = &.{0},3435 .indexes = &.{0},
3391 });3436 });
3392 const c2 = cg.allocId();3437 const c2 = cg.allocId();
3393 try cg.body.emit(gpa, .OpCompositeExtract, .{3438 try cg.body.emit(gpa, .OpCompositeExtract, .{
3394 .id_result_type = u32_ty_id,3439 .id_result_type = limb_ty_id,
3395 .id_result = c2,3440 .id_result = c2,
3396 .composite = add2,3441 .composite = add2,
3397 .indexes = &.{1},3442 .indexes = &.{1},
...@@ -3412,7 +3457,8 @@ const CompositeInt = struct {...@@ -3412,7 +3457,8 @@ const CompositeInt = struct {
3412 if (ci.info.bits == ci.info.backing_bits) return ci;3457 if (ci.info.bits == ci.info.backing_bits) return ci;
3413 const cg = ci.cg;3458 const cg = ci.cg;
3414 const gpa = cg.gpa;3459 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;
3416 assert(top_bits != 0);3462 assert(top_bits != 0);
34173463
3418 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs);3464 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs);
...@@ -3421,41 +3467,43 @@ const CompositeInt = struct {...@@ -3421,41 +3467,43 @@ const CompositeInt = struct {
3421 }3467 }
34223468
3423 const top_limb = ci.limbs[ci.n_limbs - 1];3469 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;
3424 switch (ci.info.signedness) {3472 switch (ci.info.signedness) {
3425 .unsigned => {3473 .unsigned => {
3426 const mask_val: u32 = (@as(u32, 1) << @as(u5, @intCast(top_bits))) - 1;3474 const mask_val: u64 = (@as(u64, 1) << @as(u6, @intCast(top_bits))) - 1;
3427 const mask_id = try cg.constInt(.u32, mask_val);3475 const mask_id = try cg.constInt(limb_ty, mask_val);
3428 result_limbs[ci.n_limbs - 1] = try ci.limbBinOp(.OpBitwiseAnd, top_limb, mask_id);3476 result_limbs[ci.n_limbs - 1] = try ci.limbBinOp(.OpBitwiseAnd, top_limb, mask_id);
3429 },3477 },
3430 .signed => {3478 .signed => {
3431 const u32_ty_id = try cg.resolveType(.u32, .direct);3479 const limb_ty_id = try cg.limbTypeId();
3432 const i32_ty_id = try cg.resolveType(.i32, .direct);3480 const signed_ty_id = try cg.resolveType(limb_signed_ty, .direct);
3433 const shift_amt: u32 = 32 - top_bits;3481 const shift_amt: u32 = @intCast(limb_bits - top_bits);
3434 const shift_id = try cg.constInt(.u32, shift_amt);3482 const shift_id = try cg.constInt(limb_ty, shift_amt);
34353483
3436 const as_signed = cg.allocId();3484 const as_signed = cg.allocId();
3437 try cg.body.emit(gpa, .OpBitcast, .{3485 try cg.body.emit(gpa, .OpBitcast, .{
3438 .id_result_type = i32_ty_id,3486 .id_result_type = signed_ty_id,
3439 .id_result = as_signed,3487 .id_result = as_signed,
3440 .operand = top_limb,3488 .operand = top_limb,
3441 });3489 });
3442 const shifted_left = cg.allocId();3490 const shifted_left = cg.allocId();
3443 try cg.body.emit(gpa, .OpShiftLeftLogical, .{3491 try cg.body.emit(gpa, .OpShiftLeftLogical, .{
3444 .id_result_type = i32_ty_id,3492 .id_result_type = signed_ty_id,
3445 .id_result = shifted_left,3493 .id_result = shifted_left,
3446 .base = as_signed,3494 .base = as_signed,
3447 .shift = shift_id,3495 .shift = shift_id,
3448 });3496 });
3449 const shifted_right = cg.allocId();3497 const shifted_right = cg.allocId();
3450 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{3498 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
3451 .id_result_type = i32_ty_id,3499 .id_result_type = signed_ty_id,
3452 .id_result = shifted_right,3500 .id_result = shifted_right,
3453 .base = shifted_left,3501 .base = shifted_left,
3454 .shift = shift_id,3502 .shift = shift_id,
3455 });3503 });
3456 const back = cg.allocId();3504 const back = cg.allocId();
3457 try cg.body.emit(gpa, .OpBitcast, .{3505 try cg.body.emit(gpa, .OpBitcast, .{
3458 .id_result_type = u32_ty_id,3506 .id_result_type = limb_ty_id,
3459 .id_result = back,3507 .id_result = back,
3460 .operand = shifted_right,3508 .operand = shifted_right,
3461 });3509 });
...@@ -4218,13 +4266,16 @@ const MemoryOptions = struct {...@@ -4218,13 +4266,16 @@ const MemoryOptions = struct {
4218fn needsLayout(cg: *CodeGen, as: std.lang.AddressSpace, pointee_ty: Type) bool {4266fn needsLayout(cg: *CodeGen, as: std.lang.AddressSpace, pointee_ty: Type) bool {
4219 const target = cg.zcu.getTarget();4267 const target = cg.zcu.getTarget();
4220 if (target.os.tag != .vulkan and target.os.tag != .opengl) return false;4268 if (target.os.tag != .vulkan and target.os.tag != .opengl) return false;
4221 switch (as) {4269 return switch (as) {
4222 .uniform, .push_constant, .storage_buffer => {},4270 .uniform,
4223 else => return false,4271 .push_constant,
4224 }4272 .storage_buffer,
4225 return switch (pointee_ty.zigTypeTag(cg.zcu)) {4273 .physical_storage_buffer,
4226 .@"struct", .@"union", .array => true,4274 => switch (pointee_ty.zigTypeTag(cg.zcu)) {
4227 .spirv => pointee_ty.isSpirvRuntimeArray(cg.zcu),4275 .@"struct", .@"union", .array => true,
4276 .spirv => pointee_ty.isSpirvRuntimeArray(cg.zcu),
4277 else => false,
4278 },
4228 else => false,4279 else => false,
4229 };4280 };
4230}4281}
...@@ -4537,10 +4588,6 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode...@@ -4537,10 +4588,6 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode
4537 const zcu = cg.zcu;4588 const zcu = cg.zcu;
4538 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;4589 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
4544 const base = try cg.temporary(bin_op.lhs);4591 const base = try cg.temporary(bin_op.lhs);
4545 const shift = try cg.temporary(bin_op.rhs);4592 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...@@ -4550,13 +4597,14 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode
4550 switch (info.class) {4597 switch (info.class) {
4551 .composite_integer => {4598 .composite_integer => {
4552 const shift_info = cg.arithmeticTypeInfo(shift.ty);4599 const shift_info = cg.arithmeticTypeInfo(shift.ty);
4600 const limb_ty = cg.limbType();
4553 const shift_amt_id = switch (shift_info.class) {4601 const shift_amt_id = switch (shift_info.class) {
4554 .composite_integer => blk: {4602 .composite_integer => blk: {
4555 const shift_id = try shift.materialize(cg);4603 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();
4557 const result_id = cg.allocId();4605 const result_id = cg.allocId();
4558 try cg.body.emit(cg.gpa, .OpCompositeExtract, .{4606 try cg.body.emit(cg.gpa, .OpCompositeExtract, .{
4559 .id_result_type = u32_ty_id,4607 .id_result_type = limb_ty_id,
4560 .id_result = result_id,4608 .id_result = result_id,
4561 .composite = shift_id,4609 .composite = shift_id,
4562 .indexes = &.{@as(u32, 0)},4610 .indexes = &.{@as(u32, 0)},
...@@ -4564,7 +4612,7 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode...@@ -4564,7 +4612,7 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode
4564 break :blk result_id;4612 break :blk result_id;
4565 },4613 },
4566 else => blk: {4614 else => blk: {
4567 const converted = try cg.buildConvert(.u32, shift);4615 const converted = try cg.buildConvert(limb_ty, shift);
4568 break :blk try converted.materialize(cg);4616 break :blk try converted.materialize(cg);
4569 },4617 },
4570 };4618 };
...@@ -4885,12 +4933,12 @@ fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -4885,12 +4933,12 @@ fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
4885 const is_neg = try ci.cmp(ci_z, .lt);4933 const is_neg = try ci.cmp(ci_z, .lt);
4886 const ci_neg = try ci_z.addSub(ci, false);4934 const ci_neg = try ci_z.addSub(ci, false);
4887 const result_info = cg.arithmeticTypeInfo(result_ty);4935 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();
4889 const result_limbs = try cg.id_scratch.addManyAsSlice(cg.gpa, ci.n_limbs);4937 const result_limbs = try cg.id_scratch.addManyAsSlice(cg.gpa, ci.n_limbs);
4890 for (0..ci.n_limbs) |i| {4938 for (0..ci.n_limbs) |i| {
4891 result_limbs[i] = cg.allocId();4939 result_limbs[i] = cg.allocId();
4892 try cg.body.emit(cg.gpa, .OpSelect, .{4940 try cg.body.emit(cg.gpa, .OpSelect, .{
4893 .id_result_type = u32_ty_id,4941 .id_result_type = limb_ty_id,
4894 .id_result = result_limbs[i],4942 .id_result = result_limbs[i],
4895 .condition = is_neg,4943 .condition = is_neg,
4896 .object_1 = ci_neg.limbs[i],4944 .object_1 = ci_neg.limbs[i],
...@@ -5064,12 +5112,13 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -5064,12 +5112,13 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
5064 const high_limbs = wide_limbs[ci_lhs2.n_limbs..];5112 const high_limbs = wide_limbs[ci_lhs2.n_limbs..];
50655113
5066 const bool_ty_id = try cg.resolveType(.bool, .direct);5114 const bool_ty_id = try cg.resolveType(.bool, .direct);
5067 const u32_ty_id = try cg.resolveType(.u32, .direct);5115 const limb_ty_id = try cg.limbTypeId();
5068 const n: usize = info.backing_bits / big_int_bits;5116 const limb_ty = cg.limbType();
5117 const n: usize = info.backing_bits / cg.bigIntBits();
50695118
5070 const ov_bool = switch (info.signedness) {5119 const ov_bool = switch (info.signedness) {
5071 .unsigned => blk: {5120 .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));
5073 var any_nonzero = cg.allocId();5122 var any_nonzero = cg.allocId();
5074 try cg.body.emit(gpa, .OpINotEqual, .{5123 try cg.body.emit(gpa, .OpINotEqual, .{
5075 .id_result_type = bool_ty_id,5124 .id_result_type = bool_ty_id,
...@@ -5102,32 +5151,33 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -5102,32 +5151,33 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
5102 .signed => blk: {5151 .signed => blk: {
5103 const ci_res = try CompositeInt.init(cg, result_val_id, info);5152 const ci_res = try CompositeInt.init(cg, result_val_id, info);
5104 const top_limb = ci_res.limbs[n - 1];5153 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)5157 const top_bits: u16 = if (info.bits % cg.bigIntBits() == 0)
5108 big_int_bits5158 cg.bigIntBits()
5109 else5159 else
5110 info.bits % big_int_bits;5160 info.bits % cg.bigIntBits();
51115161
5112 const shift_amt: u32 = top_bits - 1;5162 const shift_amt: u64 = top_bits - 1;
5113 const shift_id = try cg.constInt(.u32, shift_amt);5163 const shift_id = try cg.constInt(limb_ty, shift_amt);
51145164
5115 const as_signed = cg.allocId();5165 const as_signed = cg.allocId();
5116 try cg.body.emit(gpa, .OpBitcast, .{5166 try cg.body.emit(gpa, .OpBitcast, .{
5117 .id_result_type = i32_ty_id,5167 .id_result_type = signed_limb_ty_id,
5118 .id_result = as_signed,5168 .id_result = as_signed,
5119 .operand = top_limb,5169 .operand = top_limb,
5120 });5170 });
5121 const sign_ext = cg.allocId();5171 const sign_ext = cg.allocId();
5122 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{5172 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
5123 .id_result_type = i32_ty_id,5173 .id_result_type = signed_limb_ty_id,
5124 .id_result = sign_ext,5174 .id_result = sign_ext,
5125 .base = as_signed,5175 .base = as_signed,
5126 .shift = shift_id,5176 .shift = shift_id,
5127 });5177 });
5128 const expected = cg.allocId();5178 const expected = cg.allocId();
5129 try cg.body.emit(gpa, .OpBitcast, .{5179 try cg.body.emit(gpa, .OpBitcast, .{
5130 .id_result_type = u32_ty_id,5180 .id_result_type = limb_ty_id,
5131 .id_result = expected,5181 .id_result = expected,
5132 .operand = sign_ext,5182 .operand = sign_ext,
5133 });5183 });
...@@ -5160,25 +5210,25 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -5160,25 +5210,25 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
5160 }5210 }
51615211
5162 if (info.bits != info.backing_bits) {5212 if (info.bits != info.backing_bits) {
5163 const top_bits_s: u16 = info.bits % big_int_bits;5213 const top_bits_s: u16 = info.bits % cg.bigIntBits();
5164 const s_shift_id = try cg.constInt(.u32, top_bits_s - 1);5214 const s_shift_id = try cg.constInt(limb_ty, @as(u64, top_bits_s - 1));
51655215
5166 const top_as_signed = cg.allocId();5216 const top_as_signed = cg.allocId();
5167 try cg.body.emit(gpa, .OpBitcast, .{5217 try cg.body.emit(gpa, .OpBitcast, .{
5168 .id_result_type = i32_ty_id,5218 .id_result_type = signed_limb_ty_id,
5169 .id_result = top_as_signed,5219 .id_result = top_as_signed,
5170 .operand = top_limb,5220 .operand = top_limb,
5171 });5221 });
5172 const top_sign_ext = cg.allocId();5222 const top_sign_ext = cg.allocId();
5173 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{5223 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
5174 .id_result_type = i32_ty_id,5224 .id_result_type = signed_limb_ty_id,
5175 .id_result = top_sign_ext,5225 .id_result = top_sign_ext,
5176 .base = top_as_signed,5226 .base = top_as_signed,
5177 .shift = s_shift_id,5227 .shift = s_shift_id,
5178 });5228 });
5179 const top_expected = cg.allocId();5229 const top_expected = cg.allocId();
5180 try cg.body.emit(gpa, .OpBitcast, .{5230 try cg.body.emit(gpa, .OpBitcast, .{
5181 .id_result_type = u32_ty_id,5231 .id_result_type = limb_ty_id,
5182 .id_result = top_expected,5232 .id_result = top_expected,
5183 .operand = top_sign_ext,5233 .operand = top_sign_ext,
5184 });5234 });
...@@ -5219,7 +5269,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -5219,7 +5269,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
5219 // of the result too.5269 // of the result too.
52205270
5221 const target = cg.zcu.getTarget();5271 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;
5223 // If non-null, the number of bits that the multiplication should be performed in. If5273 // If non-null, the number of bits that the multiplication should be performed in. If
5224 // null, we have to use wide multiplication.5274 // null, we have to use wide multiplication.
5225 const maybe_op_ty_bits: ?u16 = switch (info.bits) {5275 const maybe_op_ty_bits: ?u16 = switch (info.bits) {
...@@ -5368,10 +5418,6 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -5368,10 +5418,6 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
5368 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5418 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5369 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;5419 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
5375 const base = try cg.temporary(extra.lhs);5421 const base = try cg.temporary(extra.lhs);
5376 const shift = try cg.temporary(extra.rhs);5422 const shift = try cg.temporary(extra.rhs);
53775423
...@@ -5964,7 +6010,27 @@ fn bitCast(...@@ -5964,7 +6010,27 @@ fn bitCast(
59646010
5965 if (src_ty.toIntern() == dst_ty.toIntern()) return src_id;6011 if (src_ty.toIntern() == dst_ty.toIntern()) return src_id;
5966 if (src_ty.isPtrAtRuntime(zcu) and dst_ty.isPtrAtRuntime(zcu)) switch (target.os.tag) {6012 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 },
5968 else => {},6034 else => {},
5969 };6035 };
59706036
...@@ -6095,15 +6161,17 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -6095,15 +6161,17 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
60956161
6096 if (src_composite and dst_composite) {6162 if (src_composite and dst_composite) {
6097 const src_id = try src.materialize(cg);6163 const src_id = try src.materialize(cg);
6098 const src_n: u16 = src_info.backing_bits / big_int_bits;6164 const limb_bits = cg.bigIntBits();
6099 const dst_n: u16 = dst_info.backing_bits / big_int_bits;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;
6100 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, dst_n);6169 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, dst_n);
6101 const min_n = @min(src_n, dst_n);6170 const min_n = @min(src_n, dst_n);
6102 const u32_ty_id = try cg.resolveType(.u32, .direct);
6103 for (0..min_n) |i| {6171 for (0..min_n) |i| {
6104 result_limbs[i] = cg.allocId();6172 result_limbs[i] = cg.allocId();
6105 try cg.body.emit(gpa, .OpCompositeExtract, .{6173 try cg.body.emit(gpa, .OpCompositeExtract, .{
6106 .id_result_type = u32_ty_id,6174 .id_result_type = limb_ty_id,
6107 .id_result = result_limbs[i],6175 .id_result = result_limbs[i],
6108 .composite = src_id,6176 .composite = src_id,
6109 .indexes = &.{@as(u32, @intCast(i))},6177 .indexes = &.{@as(u32, @intCast(i))},
...@@ -6111,30 +6179,31 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -6111,30 +6179,31 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
6111 }6179 }
6112 if (dst_n > src_n) {6180 if (dst_n > src_n) {
6113 const fill = if (src_info.signedness == .signed) blk: {6181 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);
6115 const msb = result_limbs[src_n - 1];6184 const msb = result_limbs[src_n - 1];
6116 const msb_signed = cg.allocId();6185 const msb_signed = cg.allocId();
6117 try cg.body.emit(gpa, .OpBitcast, .{6186 try cg.body.emit(gpa, .OpBitcast, .{
6118 .id_result_type = i32_ty_id,6187 .id_result_type = signed_limb_ty_id,
6119 .id_result = msb_signed,6188 .id_result = msb_signed,
6120 .operand = msb,6189 .operand = msb,
6121 });6190 });
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));
6123 const sign_ext = cg.allocId();6192 const sign_ext = cg.allocId();
6124 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{6193 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
6125 .id_result_type = i32_ty_id,6194 .id_result_type = signed_limb_ty_id,
6126 .id_result = sign_ext,6195 .id_result = sign_ext,
6127 .base = msb_signed,6196 .base = msb_signed,
6128 .shift = shift31,6197 .shift = shift_amt,
6129 });6198 });
6130 const back = cg.allocId();6199 const back = cg.allocId();
6131 try cg.body.emit(gpa, .OpBitcast, .{6200 try cg.body.emit(gpa, .OpBitcast, .{
6132 .id_result_type = u32_ty_id,6201 .id_result_type = limb_ty_id,
6133 .id_result = back,6202 .id_result = back,
6134 .operand = sign_ext,6203 .operand = sign_ext,
6135 });6204 });
6136 break :blk back;6205 break :blk back;
6137 } else try cg.constInt(.u32, @as(u32, 0));6206 } else try cg.constInt(limb_ty, @as(u64, 0));
6138 for (min_n..dst_n) |i| {6207 for (min_n..dst_n) |i| {
6139 result_limbs[i] = fill;6208 result_limbs[i] = fill;
6140 }6209 }
...@@ -6144,16 +6213,18 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -6144,16 +6213,18 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
6144 return try normalized.materialize(dst_ty);6213 return try normalized.materialize(dst_ty);
6145 } else if (src_composite and !dst_composite) {6214 } else if (src_composite and !dst_composite) {
6146 const src_id = try src.materialize(cg);6215 const src_id = try src.materialize(cg);
6147 const u32_ty_id = try cg.resolveType(.u32, .direct);6216 const limb_bits = cg.bigIntBits();
6148 if (dst_info.backing_bits <= 32) {6217 const limb_ty = cg.limbType();
6218 const limb_ty_id = try cg.limbTypeId();
6219 if (dst_info.backing_bits <= limb_bits) {
6149 const limb0 = cg.allocId();6220 const limb0 = cg.allocId();
6150 try cg.body.emit(gpa, .OpCompositeExtract, .{6221 try cg.body.emit(gpa, .OpCompositeExtract, .{
6151 .id_result_type = u32_ty_id,6222 .id_result_type = limb_ty_id,
6152 .id_result = limb0,6223 .id_result = limb0,
6153 .composite = src_id,6224 .composite = src_id,
6154 .indexes = &.{@as(u32, 0)},6225 .indexes = &.{@as(u32, 0)},
6155 });6226 });
6156 const tmp: Temporary = .init(.u32, limb0);6227 const tmp: Temporary = .init(limb_ty, limb0);
6157 const converted = try cg.buildConvert(dst_ty, tmp);6228 const converted = try cg.buildConvert(dst_ty, tmp);
6158 const result = if (dst_info.bits < src_info.bits)6229 const result = if (dst_info.bits < src_info.bits)
6159 try cg.normalize(converted, dst_info)6230 try cg.normalize(converted, dst_info)
...@@ -6161,16 +6232,17 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -6161,16 +6232,17 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
6161 converted;6232 converted;
6162 return try result.materialize(cg);6233 return try result.materialize(cg);
6163 } else {6234 } else {
6235 assert(limb_bits == 32); // dst > 64 while limbs are 64 shouldn't happen — dst fits in one 64-bit limb.
6164 const limb0 = cg.allocId();6236 const limb0 = cg.allocId();
6165 try cg.body.emit(gpa, .OpCompositeExtract, .{6237 try cg.body.emit(gpa, .OpCompositeExtract, .{
6166 .id_result_type = u32_ty_id,6238 .id_result_type = limb_ty_id,
6167 .id_result = limb0,6239 .id_result = limb0,
6168 .composite = src_id,6240 .composite = src_id,
6169 .indexes = &.{@as(u32, 0)},6241 .indexes = &.{@as(u32, 0)},
6170 });6242 });
6171 const limb1 = cg.allocId();6243 const limb1 = cg.allocId();
6172 try cg.body.emit(gpa, .OpCompositeExtract, .{6244 try cg.body.emit(gpa, .OpCompositeExtract, .{
6173 .id_result_type = u32_ty_id,6245 .id_result_type = limb_ty_id,
6174 .id_result = limb1,6246 .id_result = limb1,
6175 .composite = src_id,6247 .composite = src_id,
6176 .indexes = &.{@as(u32, 1)},6248 .indexes = &.{@as(u32, 1)},
...@@ -6212,19 +6284,21 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -6212,19 +6284,21 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
6212 return try result.materialize(cg);6284 return try result.materialize(cg);
6213 }6285 }
6214 } else {6286 } 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;
6216 const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, dst_n);6291 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) {6293 if (src_info.backing_bits <= limb_bits) {
6220 const converted = try cg.buildConvert(.u32, src);6294 const converted = try cg.buildConvert(limb_ty, src);
6221 result_limbs[0] = try converted.materialize(cg);6295 result_limbs[0] = try converted.materialize(cg);
6222 } else {6296 } else {
6223 const src_as_u64 = try cg.buildConvert(.u64, src);6297 const src_as_u64 = try cg.buildConvert(.u64, src);
6224 const src_id = try src_as_u64.materialize(cg);6298 const src_id = try src_as_u64.materialize(cg);
6225 result_limbs[0] = cg.allocId();6299 result_limbs[0] = cg.allocId();
6226 try cg.body.emit(gpa, .OpUConvert, .{6300 try cg.body.emit(gpa, .OpUConvert, .{
6227 .id_result_type = u32_ty_id,6301 .id_result_type = limb_ty_id,
6228 .id_result = result_limbs[0],6302 .id_result = result_limbs[0],
6229 .unsigned_value = src_id,6303 .unsigned_value = src_id,
6230 });6304 });
...@@ -6239,38 +6313,39 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -6239,38 +6313,39 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
6239 });6313 });
6240 result_limbs[1] = cg.allocId();6314 result_limbs[1] = cg.allocId();
6241 try cg.body.emit(gpa, .OpUConvert, .{6315 try cg.body.emit(gpa, .OpUConvert, .{
6242 .id_result_type = u32_ty_id,6316 .id_result_type = limb_ty_id,
6243 .id_result = result_limbs[1],6317 .id_result = result_limbs[1],
6244 .unsigned_value = hi,6318 .unsigned_value = hi,
6245 });6319 });
6246 }6320 }
6247 // Sign/zero-extend remaining limbs.6321 // 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;
6249 const fill = if (src_info.signedness == .signed) blk: {6323 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);
6251 const msb = result_limbs[fill_start - 1];6326 const msb = result_limbs[fill_start - 1];
6252 const msb_signed = cg.allocId();6327 const msb_signed = cg.allocId();
6253 try cg.body.emit(gpa, .OpBitcast, .{6328 try cg.body.emit(gpa, .OpBitcast, .{
6254 .id_result_type = i32_ty_id,6329 .id_result_type = signed_limb_ty_id,
6255 .id_result = msb_signed,6330 .id_result = msb_signed,
6256 .operand = msb,6331 .operand = msb,
6257 });6332 });
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));
6259 const sign_ext = cg.allocId();6334 const sign_ext = cg.allocId();
6260 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{6335 try cg.body.emit(gpa, .OpShiftRightArithmetic, .{
6261 .id_result_type = i32_ty_id,6336 .id_result_type = signed_limb_ty_id,
6262 .id_result = sign_ext,6337 .id_result = sign_ext,
6263 .base = msb_signed,6338 .base = msb_signed,
6264 .shift = shift31,6339 .shift = shift_amt,
6265 });6340 });
6266 const back = cg.allocId();6341 const back = cg.allocId();
6267 try cg.body.emit(gpa, .OpBitcast, .{6342 try cg.body.emit(gpa, .OpBitcast, .{
6268 .id_result_type = u32_ty_id,6343 .id_result_type = limb_ty_id,
6269 .id_result = back,6344 .id_result = back,
6270 .operand = sign_ext,6345 .operand = sign_ext,
6271 });6346 });
6272 break :blk back;6347 break :blk back;
6273 } else try cg.constInt(.u32, @as(u32, 0));6348 } else try cg.constInt(limb_ty, @as(u64, 0));
6274 for (fill_start..dst_n) |i| {6349 for (fill_start..dst_n) |i| {
6275 result_limbs[i] = fill;6350 result_limbs[i] = fill;
6276 }6351 }
...@@ -8632,37 +8707,68 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -8632,37 +8707,68 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
8632 const input_ty = cg.typeOf(in.operand);8707 const input_ty = cg.typeOf(in.operand);
86338708
8634 if (std.mem.eql(u8, in.constraint, "c")) {8709 if (std.mem.eql(u8, in.constraint, "c")) {
8635 // constant8710 const val: Value = .fromInterned(in.operand.toInterned().?);
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
8640 const ip = &zcu.intern_pool;8711 const ip = &zcu.intern_pool;
8641 switch (ip.indexToKey(val.toIntern())) {8712 const target = cg.pt.zcu.getTarget();
8642 .int_type,8713 switch (input_ty.zigTypeTag(zcu)) {
8643 .ptr_type,8714 .int => {
8644 .array_type,8715 const bits: u64 = switch (input_ty.intInfo(zcu).signedness) {
8645 .vector_type,8716 .unsigned => val.toUnsignedInt(zcu),
8646 .opt_type,8717 .signed => @bitCast(val.toSignedInt(zcu)),
8647 .anyframe_type,8718 };
8648 .error_union_type,8719 try ass.value_map.put(gpa, in.name, .{ .constant = bits });
8649 .simple_type,8720 },
8650 .struct_type,8721 .float => {
8651 .union_type,8722 const bits: u64 = switch (input_ty.floatBits(target)) {
8652 .opaque_type,8723 16 => @as(u16, @bitCast(val.toFloat(f16, zcu))),
8653 .spirv_type,8724 32 => @as(u32, @bitCast(val.toFloat(f32, zcu))),
8654 .enum_type,8725 64 => @bitCast(val.toFloat(f64, zcu)),
8655 .func_type,8726 else => unreachable, // Sema rejects unsupported float widths.
8656 .error_set_type,8727 };
8657 .inferred_error_set_type,8728 try ass.value_map.put(gpa, in.name, .{ .constant = bits });
8658 => unreachable, // types, not values8729 },
86598730 .vector => {
8660 .undef => return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}),8731 const child_ty = input_ty.childType(zcu);
86618732 const child_kind = child_ty.zigTypeTag(zcu);
8662 .int => try ass.value_map.put(gpa, in.name, .{ .constant = @intCast(val.toUnsignedInt(zcu)) }),8733 const child_bit_width: u16 = switch (child_kind) {
8663 .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }),8734 .bool => 0,
86648735 .int => @intCast(child_ty.intInfo(zcu).bits),
8665 else => unreachable, // TODO8736 .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.
8666 }8772 }
8667 } else if (std.mem.eql(u8, in.constraint, "t")) {8773 } else if (std.mem.eql(u8, in.constraint, "t")) {
8668 // type8774 // type
...@@ -8729,7 +8835,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -8729,7 +8835,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
8729 .just_declared, .unresolved_forward_reference => unreachable,8835 .just_declared, .unresolved_forward_reference => unreachable,
8730 .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}),8836 .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}),
8731 .value => |ref| return ref,8837 .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", .{}),
8733 }8839 }
8734 // TODO: Multiple results8840 // TODO: Multiple results
8735 // TODO: Check that the output type from assembly is the same as the type actually expected by Zig.8841 // 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)...@@ -8749,8 +8855,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
8749 const callee_ty = cg.typeOf(air_call.callee);8855 const callee_ty = cg.typeOf(air_call.callee);
8750 const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) {8856 const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) {
8751 .@"fn" => callee_ty,8857 .@"fn" => callee_ty,
8752 .pointer => return cg.fail("cannot call function pointers", .{}),8858 else => unreachable, // rejected by Sema for SPIR-V
8753 else => unreachable,
8754 };8859 };
8755 const fn_info = zcu.typeToFunc(zig_fn_ty).?;8860 const fn_info = zcu.typeToFunc(zig_fn_ty).?;
8756 const return_type = fn_info.return_type;8861 const return_type = fn_info.return_type;
src/link/SpirV.zig+6-3
...@@ -621,9 +621,12 @@ fn emitPreamble(...@@ -621,9 +621,12 @@ fn emitPreamble(
621 },621 },
622 else => unreachable,622 else => unreachable,
623 }623 }
624 if (target.os.tag == .vulkan and target.cpu.arch == .spirv64) {624 if (target.cpu.arch == .spirv64) {
625 caps.insert(.physical_storage_buffer_addresses);625 caps.insert(.int64);
626 try exts.put(gpa, "SPV_KHR_physical_storage_buffer", {});626 if (target.os.tag == .vulkan) {
627 caps.insert(.physical_storage_buffer_addresses);
628 try exts.put(gpa, "SPV_KHR_physical_storage_buffer", {});
629 }
627 }630 }
628 if (has_linkage) caps.insert(.linkage);631 if (has_linkage) caps.insert(.linkage);
629632
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;
test/behavior/spirv.zig+9
...@@ -49,6 +49,15 @@ test "@SpirvType" {...@@ -49,6 +49,15 @@ test "@SpirvType" {
49 _ = runtime_array;49 _ = runtime_array;
50}50}
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
52test "@SpirvType equality" {61test "@SpirvType equality" {
53 try expect(@SpirvType(.sampler) == Sampler);62 try expect(@SpirvType(.sampler) == Sampler);
54 try expect(@SpirvType(.{ .runtime_array = u32 }) == RuntimeArray);63 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, .{...@@ -6,11 +6,11 @@ const buf = @extern(*addrspace(.storage_buffer) Buffer, .{
6 .name = "buf",6 .name = "buf",
7 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },7 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
8});8});
9export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void {9export fn main() callconv(.kernel) void {
10 const a = buf.data;10 const a = buf.data;
11 _ = a;11 _ = a;
12}12}
13export fn main2() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void {13export fn main2() callconv(.kernel) void {
14 const p: *addrspace(.storage_buffer) const RuntimeArray = &buf.data;14 const p: *addrspace(.storage_buffer) const RuntimeArray = &buf.data;
15 _ = p.*;15 _ = p.*;
16}16}
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