| author | |
| committer | |
| log | c498b66434412616a97328a7f7a05014aa5183dc |
| tree | 5bd059bc5bb8bf9c3629e7706d56343571418011 |
| parent | 332c73ccd2246af417afaa42597055c9ba6255c1 |
add `std.spirv.specConst()` and support lowering literal values as string in inline assembly4 files changed, 262 insertions(+), 48 deletions(-)
lib/std/spirv.zig+30| ... | @@ -82,3 +82,33 @@ pub fn workgroupBarrier() void { | ... | @@ -82,3 +82,33 @@ pub fn workgroupBarrier() void { |
| 82 | .{ .acquire_release = true, .workgroup_memory = true }, | 82 | .{ .acquire_release = true, .workgroup_memory = true }, |
| 83 | ); | 83 | ); |
| 84 | } | 84 | } |
| 85 | |||
| 86 | pub fn specConst(T: type, comptime default_value: T, comptime spec_id: u32) T { | ||
| 87 | switch (@typeInfo(T)) { | ||
| 88 | .bool => { | ||
| 89 | const op = if (default_value) "OpSpecConstantTrue" else "OpSpecConstantFalse"; | ||
| 90 | return asm ("%ret = " ++ op ++ " %ty\n" ++ | ||
| 91 | "OpDecorate %ret SpecId $spec_id" | ||
| 92 | : [ret] "" (-> T), | ||
| 93 | : [ty] "t" (T), | ||
| 94 | [spec_id] "c" (spec_id), | ||
| 95 | ); | ||
| 96 | }, | ||
| 97 | .int, .float => return asm ( | ||
| 98 | \\%ret = OpSpecConstant %ty $default_value | ||
| 99 | \\OpDecorate %ret SpecId $spec_id" | ||
| 100 | : [ret] "" (-> T), | ||
| 101 | : [ty] "t" (T), | ||
| 102 | [default_value] "c" (default_value), | ||
| 103 | [spec_id] "c" (spec_id), | ||
| 104 | ), | ||
| 105 | .vector => return asm ( | ||
| 106 | \\%ret = OpSpecConstantComposite %ty %default_value %spec_id | ||
| 107 | : [ret] "" (-> T), | ||
| 108 | : [ty] "t" (T), | ||
| 109 | [default_value] "c" (default_value), | ||
| 110 | [spec_id] "c" (spec_id), | ||
| 111 | ), | ||
| 112 | else => @compileError("unsupported spec constant type"), | ||
| 113 | } | ||
| 114 | } |
src/codegen/spirv/Assembler.zig+153-7| ... | @@ -58,6 +58,10 @@ const Operand = union(enum) { | ... | @@ -58,6 +58,10 @@ const Operand = union(enum) { |
| 58 | pub fn deinit(ass: *Assembler) void { | 58 | pub 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, |
| 138 | 152 | ||
| 139 | /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue | 153 | /// 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 | } |
| 400 | 421 | ||
| 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. | ||
| 425 | fn 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 | |||
| 401 | fn resolveMaybeForwardRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue { | 503 | fn 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); |
| 816 | 943 | ||
| 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); |
| 819 | 965 | ||
| 820 | const text = ass.tokenText(tok); | 966 | const text = ass.tokenText(tok); |
src/codegen/spirv/CodeGen.zig+78-40| ... | @@ -423,7 +423,7 @@ pub fn addEntryPointDeps( | ... | @@ -423,7 +423,7 @@ pub fn addEntryPointDeps( |
| 423 | cg: *CodeGen, | 423 | cg: *CodeGen, |
| 424 | decl_index: Decl.Index, | 424 | decl_index: Decl.Index, |
| 425 | seen: *std.bit_set.Dynamic, | 425 | seen: *std.bit_set.Dynamic, |
| 426 | interface: *std.array_list.Managed(Id), | 426 | interface: *std.ArrayList(Id), |
| 427 | ) !void { | 427 | ) !void { |
| 428 | const decl = cg.declPtr(decl_index); | 428 | const decl = cg.declPtr(decl_index); |
| 429 | const deps = cg.decl_deps.items[decl.begin_dep..decl.end_dep]; | 429 | const deps = cg.decl_deps.items[decl.begin_dep..decl.end_dep]; |
| ... | @@ -435,7 +435,7 @@ pub fn addEntryPointDeps( | ... | @@ -435,7 +435,7 @@ pub fn addEntryPointDeps( |
| 435 | seen.set(@intFromEnum(decl_index)); | 435 | seen.set(@intFromEnum(decl_index)); |
| 436 | 436 | ||
| 437 | if (decl.kind == .global) { | 437 | if (decl.kind == .global) { |
| 438 | try interface.append(decl.result_id); | 438 | try interface.append(cg.gpa, decl.result_id); |
| 439 | } | 439 | } |
| 440 | 440 | ||
| 441 | for (deps) |dep| { | 441 | for (deps) |dep| { |
| ... | @@ -1806,11 +1806,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { | ... | @@ -1806,11 +1806,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { |
| 1806 | const struct_type = zcu.typeToStruct(ty).?; | 1806 | const struct_type = zcu.typeToStruct(ty).?; |
| 1807 | assert(struct_type.layout != .@"packed"); // packed structs use `bitpack` | 1807 | assert(struct_type.layout != .@"packed"); // packed structs use `bitpack` |
| 1808 | 1808 | ||
| 1809 | var types = std.array_list.Managed(Type).init(gpa); | 1809 | var types: std.ArrayList(Type) = .empty; |
| 1810 | defer types.deinit(); | 1810 | defer types.deinit(gpa); |
| 1811 | 1811 | ||
| 1812 | var constituents = std.array_list.Managed(Id).init(gpa); | 1812 | var constituents: std.ArrayList(Id) = .empty; |
| 1813 | defer constituents.deinit(); | 1813 | defer constituents.deinit(gpa); |
| 1814 | 1814 | ||
| 1815 | var it = struct_type.iterateRuntimeOrder(ip); | 1815 | var it = struct_type.iterateRuntimeOrder(ip); |
| 1816 | while (it.next()) |field_index| { | 1816 | while (it.next()) |field_index| { |
| ... | @@ -1824,8 +1824,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { | ... | @@ -1824,8 +1824,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { |
| 1824 | const field_val = try val.fieldValue(pt, field_index); | 1824 | const field_val = try val.fieldValue(pt, field_index); |
| 1825 | const field_id = try cg.constant(field_ty, field_val, .indirect); | 1825 | const field_id = try cg.constant(field_ty, field_val, .indirect); |
| 1826 | 1826 | ||
| 1827 | try types.append(field_ty); | 1827 | try types.append(gpa, field_ty); |
| 1828 | try constituents.append(field_id); | 1828 | try constituents.append(gpa, field_id); |
| 1829 | } | 1829 | } |
| 1830 | 1830 | ||
| 1831 | const comp_ty_id = try cg.resolveType(ty, .direct); | 1831 | const comp_ty_id = try cg.resolveType(ty, .direct); |
| ... | @@ -2366,11 +2366,11 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { | ... | @@ -2366,11 +2366,11 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 2366 | return try cg.resolveType(.fromInterned(struct_type.packed_backing_int_type), .direct); | 2366 | return try cg.resolveType(.fromInterned(struct_type.packed_backing_int_type), .direct); |
| 2367 | } | 2367 | } |
| 2368 | 2368 | ||
| 2369 | var member_types = std.array_list.Managed(Id).init(gpa); | 2369 | var member_types: std.ArrayList(Id) = .empty; |
| 2370 | defer member_types.deinit(); | 2370 | defer member_types.deinit(gpa); |
| 2371 | 2371 | ||
| 2372 | var member_names = std.array_list.Managed([]const u8).init(gpa); | 2372 | var member_names: std.ArrayList([]const u8) = .empty; |
| 2373 | defer member_names.deinit(); | 2373 | defer member_names.deinit(gpa); |
| 2374 | 2374 | ||
| 2375 | var it = struct_type.iterateRuntimeOrder(ip); | 2375 | var it = struct_type.iterateRuntimeOrder(ip); |
| 2376 | while (it.next()) |field_index| { | 2376 | while (it.next()) |field_index| { |
| ... | @@ -2378,8 +2378,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { | ... | @@ -2378,8 +2378,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 2378 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 2378 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 2379 | 2379 | ||
| 2380 | const field_name = struct_type.field_names.get(ip)[field_index]; | 2380 | const field_name = struct_type.field_names.get(ip)[field_index]; |
| 2381 | try member_types.append(try cg.resolveType(field_ty, .indirect)); | 2381 | try member_types.append(gpa, try cg.resolveType(field_ty, .indirect)); |
| 2382 | try member_names.append(field_name.toSlice(ip)); | 2382 | try member_names.append(gpa, field_name.toSlice(ip)); |
| 2383 | } | 2383 | } |
| 2384 | 2384 | ||
| 2385 | const result_id = try cg.structType( | 2385 | const result_id = try cg.structType( |
| ... | @@ -8688,31 +8688,69 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -8688,31 +8688,69 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 8688 | }); | 8688 | }); |
| 8689 | 8689 | ||
| 8690 | const ip = &zcu.intern_pool; | 8690 | const ip = &zcu.intern_pool; |
| 8691 | switch (ip.indexToKey(val.toIntern())) { | 8691 | const target = cg.pt.zcu.getTarget(); |
| 8692 | .int_type, | 8692 | if (ip.indexToKey(val.toIntern()) == .undef) { |
| 8693 | .ptr_type, | 8693 | return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}); |
| 8694 | .array_type, | 8694 | } |
| 8695 | .vector_type, | 8695 | switch (input_ty.zigTypeTag(zcu)) { |
| 8696 | .opt_type, | 8696 | .int => { |
| 8697 | .anyframe_type, | 8697 | const bits: u64 = switch (input_ty.intInfo(zcu).signedness) { |
| 8698 | .error_union_type, | 8698 | .unsigned => val.toUnsignedInt(zcu), |
| 8699 | .simple_type, | 8699 | .signed => @bitCast(val.toSignedInt(zcu)), |
| 8700 | .struct_type, | 8700 | }; |
| 8701 | .union_type, | 8701 | try ass.value_map.put(gpa, in.name, .{ .constant = bits }); |
| 8702 | .opaque_type, | 8702 | }, |
| 8703 | .spirv_type, | 8703 | .float => { |
| 8704 | .enum_type, | 8704 | const bits: u64 = switch (input_ty.floatBits(target)) { |
| 8705 | .func_type, | 8705 | 16 => @as(u16, @bitCast(val.toFloat(f16, zcu))), |
| 8706 | .error_set_type, | 8706 | 32 => @as(u32, @bitCast(val.toFloat(f32, zcu))), |
| 8707 | .inferred_error_set_type, | 8707 | 64 => @bitCast(val.toFloat(f64, zcu)), |
| 8708 | => unreachable, // types, not values | 8708 | else => return cg.fail("unsupported float width for 'c' constraint", .{}), |
| 8709 | 8709 | }; | |
| 8710 | .undef => return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}), | 8710 | try ass.value_map.put(gpa, in.name, .{ .constant = bits }); |
| 8711 | 8711 | }, | |
| 8712 | .int => try ass.value_map.put(gpa, in.name, .{ .constant = @intCast(val.toUnsignedInt(zcu)) }), | 8712 | .vector => { |
| 8713 | .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }), | 8713 | const child_ty = input_ty.childType(zcu); |
| 8714 | 8714 | const child_kind = child_ty.zigTypeTag(zcu); | |
| 8715 | else => unreachable, // TODO | 8715 | const child_bit_width: u16 = switch (child_kind) { |
| 8716 | .bool => 0, | ||
| 8717 | .int => @intCast(child_ty.intInfo(zcu).bits), | ||
| 8718 | .float => child_ty.floatBits(target), | ||
| 8719 | else => return cg.fail("'c' constraint vector element must be bool, int, or float", .{}), | ||
| 8720 | }; | ||
| 8721 | const vec_len: usize = @intCast(input_ty.vectorLen(zcu)); | ||
| 8722 | const values = try gpa.alloc(u64, vec_len); | ||
| 8723 | errdefer gpa.free(values); | ||
| 8724 | for (values, 0..) |*out, i| { | ||
| 8725 | const elem: Value = try val.elemValue(cg.pt, i); | ||
| 8726 | out.* = switch (child_kind) { | ||
| 8727 | .bool => @intFromBool(elem.toBool()), | ||
| 8728 | .int => switch (child_ty.intInfo(zcu).signedness) { | ||
| 8729 | .unsigned => elem.toUnsignedInt(zcu), | ||
| 8730 | .signed => @bitCast(elem.toSignedInt(zcu)), | ||
| 8731 | }, | ||
| 8732 | .float => switch (child_bit_width) { | ||
| 8733 | 16 => @as(u16, @bitCast(elem.toFloat(f16, zcu))), | ||
| 8734 | 32 => @as(u32, @bitCast(elem.toFloat(f32, zcu))), | ||
| 8735 | 64 => @bitCast(elem.toFloat(f64, zcu)), | ||
| 8736 | else => unreachable, | ||
| 8737 | }, | ||
| 8738 | else => unreachable, | ||
| 8739 | }; | ||
| 8740 | } | ||
| 8741 | const child_ty_id = try cg.resolveType(child_ty, .direct); | ||
| 8742 | try ass.value_map.put(gpa, in.name, .{ .constant_composite = .{ | ||
| 8743 | .child = child_ty_id, | ||
| 8744 | .child_kind = child_kind, | ||
| 8745 | .child_bit_width = child_bit_width, | ||
| 8746 | .values = values, | ||
| 8747 | } }); | ||
| 8748 | }, | ||
| 8749 | .@"enum" => switch (ip.indexToKey(val.toIntern())) { | ||
| 8750 | .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }), | ||
| 8751 | else => unreachable, | ||
| 8752 | }, | ||
| 8753 | else => return cg.fail("unsupported type for 'c' constraint", .{}), | ||
| 8716 | } | 8754 | } |
| 8717 | } else if (std.mem.eql(u8, in.constraint, "t")) { | 8755 | } else if (std.mem.eql(u8, in.constraint, "t")) { |
| 8718 | // type | 8756 | // type |
| ... | @@ -8779,7 +8817,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -8779,7 +8817,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 8779 | .just_declared, .unresolved_forward_reference => unreachable, | 8817 | .just_declared, .unresolved_forward_reference => unreachable, |
| 8780 | .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}), | 8818 | .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}), |
| 8781 | .value => |ref| return ref, | 8819 | .value => |ref| return ref, |
| 8782 | .constant, .string => return cg.fail("cannot return constant from assembly", .{}), | 8820 | .constant, .constant_composite, .string => return cg.fail("cannot return constant from assembly", .{}), |
| 8783 | } | 8821 | } |
| 8784 | // TODO: Multiple results | 8822 | // TODO: Multiple results |
| 8785 | // TODO: Check that the output type from assembly is the same as the type actually expected by Zig. | 8823 | // TODO: Check that the output type from assembly is the same as the type actually expected by Zig. |
src/link/SpirV/BinaryModule.zig+1-1| ... | @@ -303,7 +303,7 @@ pub const Parser = struct { | ... | @@ -303,7 +303,7 @@ pub const Parser = struct { |
| 303 | } | 303 | } |
| 304 | }, | 304 | }, |
| 305 | .literal_context_dependent_number => { | 305 | .literal_context_dependent_number => { |
| 306 | assert(inst.opcode == .OpConstant or inst.opcode == .OpSpecConstantOp); | 306 | assert(inst.opcode == .OpConstant or inst.opcode == .OpSpecConstant); |
| 307 | const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse { | 307 | const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse { |
| 308 | log.err("invalid LiteralContextDependentNumber type {}", .{inst.operands[0]}); | 308 | log.err("invalid LiteralContextDependentNumber type {}", .{inst.operands[0]}); |
| 309 | return error.InvalidId; | 309 | return error.InvalidId; |