| ... | ... | @@ -459,6 +459,22 @@ pub const DeclGen = struct { |
| 459 | 459 | return try self.intType(.unsigned, self.getTarget().cpu.arch.ptrBitWidth()); |
| 460 | 460 | } |
| 461 | 461 | |
| 462 | /// Construct a simple struct type which consists of some members, and no decorations. |
| 463 | /// `members` lifetime only needs to last for this function as it is copied. |
| 464 | fn simpleStructType(self: *DeclGen, members: []const SpvType.Payload.Struct.Member) !SpvType.Ref { |
| 465 | const payload = try self.spv.arena.create(SpvType.Payload.Struct); |
| 466 | payload.* = .{ |
| 467 | .members = try self.spv.arena.dupe(SpvType.Payload.Struct.Member, members), |
| 468 | .decorations = .{}, |
| 469 | }; |
| 470 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); |
| 471 | } |
| 472 | |
| 473 | fn simpleStructTypeId(self: *DeclGen, members: []const SpvType.Payload.Struct.Member) !IdResultType { |
| 474 | const type_ref = try self.simpleStructType(members); |
| 475 | return self.spv.typeResultId(type_ref); |
| 476 | } |
| 477 | |
| 462 | 478 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. |
| 463 | 479 | fn resolveType(self: *DeclGen, ty: Type) Error!SpvType.Ref { |
| 464 | 480 | const target = self.getTarget(); |
| ... | ... | @@ -555,25 +571,10 @@ pub const DeclGen = struct { |
| 555 | 571 | const len_align = len_ty.abiAlignment(target); |
| 556 | 572 | const len_offset = std.mem.alignForwardGeneric(u64, ptr_size, len_align); |
| 557 | 573 | |
| 558 | | const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, 2); |
| 559 | | members[0] = .{ |
| 560 | | .ty = spv_ptr_ty, |
| 561 | | .offset = 0, |
| 562 | | .decorations = .{}, |
| 563 | | }; |
| 564 | | members[1] = .{ |
| 565 | | .ty = try self.sizeType(), |
| 566 | | .offset = @intCast(u32, len_offset), |
| 567 | | .decorations = .{}, |
| 568 | | }; |
| 569 | | |
| 570 | | const slice_payload = try self.spv.arena.create(SpvType.Payload.Struct); |
| 571 | | slice_payload.* = .{ |
| 572 | | .members = members, |
| 573 | | .decorations = .{}, |
| 574 | | .member_decoration_extra = &.{}, |
| 575 | | }; |
| 576 | | return try self.spv.resolveType(SpvType.initPayload(&slice_payload.base)); |
| 574 | return try self.simpleStructType(&.{ |
| 575 | .{ .ty = spv_ptr_ty, .offset = 0 }, |
| 576 | .{ .ty = try self.sizeType(), .offset = @intCast(u32, len_offset) }, |
| 577 | }); |
| 577 | 578 | }, |
| 578 | 579 | .Vector => { |
| 579 | 580 | // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations |
| ... | ... | @@ -594,7 +595,24 @@ pub const DeclGen = struct { |
| 594 | 595 | }, |
| 595 | 596 | .Struct => { |
| 596 | 597 | if (ty.isSimpleTupleOrAnonStruct()) { |
| 597 | | return self.todo("implement tuple struct type", .{}); |
| 598 | const tuple = ty.tupleFields(); |
| 599 | const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, tuple.types.len); |
| 600 | var member_index: usize = 0; |
| 601 | for (tuple.types) |field_ty, i| { |
| 602 | const field_val = tuple.values[i]; |
| 603 | if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue; |
| 604 | |
| 605 | members[member_index] = .{ |
| 606 | .ty = try self.resolveType(field_ty), |
| 607 | .offset = 0, |
| 608 | }; |
| 609 | } |
| 610 | |
| 611 | const payload = try self.spv.arena.create(SpvType.Payload.Struct); |
| 612 | payload.* = .{ |
| 613 | .members = members[0..member_index], |
| 614 | }; |
| 615 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); |
| 598 | 616 | } |
| 599 | 617 | |
| 600 | 618 | const struct_ty = ty.castTag(.@"struct").?.data; |
| ... | ... | @@ -611,15 +629,12 @@ pub const DeclGen = struct { |
| 611 | 629 | members[member_index] = .{ |
| 612 | 630 | .ty = try self.resolveType(field.ty), |
| 613 | 631 | .offset = field.offset, |
| 614 | | .decorations = .{}, |
| 615 | 632 | }; |
| 616 | 633 | } |
| 617 | 634 | |
| 618 | 635 | const payload = try self.spv.arena.create(SpvType.Payload.Struct); |
| 619 | 636 | payload.* = .{ |
| 620 | 637 | .members = members[0..member_index], |
| 621 | | .decorations = .{}, |
| 622 | | .member_decoration_extra = &.{}, |
| 623 | 638 | }; |
| 624 | 639 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); |
| 625 | 640 | }, |
| ... | ... | @@ -709,6 +724,8 @@ pub const DeclGen = struct { |
| 709 | 724 | .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub), |
| 710 | 725 | .mul, .mulwrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul), |
| 711 | 726 | |
| 727 | .add_with_overflow => try self.airOverflowArithOp(inst), |
| 728 | |
| 712 | 729 | .shuffle => try self.airShuffle(inst), |
| 713 | 730 | |
| 714 | 731 | .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), |
| ... | ... | @@ -719,6 +736,7 @@ pub const DeclGen = struct { |
| 719 | 736 | |
| 720 | 737 | .bitcast => try self.airBitcast(inst), |
| 721 | 738 | .not => try self.airNot(inst), |
| 739 | |
| 722 | 740 | .slice_ptr => try self.airSliceField(inst, 0), |
| 723 | 741 | .slice_len => try self.airSliceField(inst, 1), |
| 724 | 742 | .slice_elem_ptr => try self.airSliceElemPtr(inst), |
| ... | ... | @@ -841,6 +859,82 @@ pub const DeclGen = struct { |
| 841 | 859 | return result_id.toRef(); |
| 842 | 860 | } |
| 843 | 861 | |
| 862 | fn airOverflowArithOp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 863 | if (self.liveness.isUnused(inst)) return null; |
| 864 | |
| 865 | const target = self.getTarget(); |
| 866 | |
| 867 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 868 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 869 | const lhs = try self.resolve(extra.lhs); |
| 870 | const rhs = try self.resolve(extra.rhs); |
| 871 | |
| 872 | const operand_ty = self.air.typeOf(extra.lhs); |
| 873 | const result_ty = self.air.typeOfIndex(inst); |
| 874 | |
| 875 | const operand_ty_id = try self.resolveTypeId(operand_ty); |
| 876 | const result_type_id = try self.resolveTypeId(result_ty); |
| 877 | |
| 878 | const operand_bits = operand_ty.intInfo(target).bits; |
| 879 | const overflow_member_ty = try self.intType(.unsigned, operand_bits); |
| 880 | const overflow_member_ty_id = self.spv.typeResultId(overflow_member_ty); |
| 881 | |
| 882 | const op_result_id = blk: { |
| 883 | // Construct the SPIR-V result type. |
| 884 | // It is almost the same as the zig one, except that the fields must be the same type |
| 885 | // and they must be unsigned. |
| 886 | const overflow_result_ty = try self.simpleStructTypeId(&.{ |
| 887 | .{ .ty = overflow_member_ty, .offset = 0 }, |
| 888 | .{ .ty = overflow_member_ty, .offset = @intCast(u32, operand_ty.abiSize(target)) }, |
| 889 | }); |
| 890 | const result_id = self.spv.allocId(); |
| 891 | try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{ |
| 892 | .id_result_type = overflow_result_ty, |
| 893 | .id_result = result_id, |
| 894 | .operand_1 = lhs, |
| 895 | .operand_2 = rhs, |
| 896 | }); |
| 897 | break :blk result_id.toRef(); |
| 898 | }; |
| 899 | |
| 900 | // Now convert the SPIR-V flavor result into a Zig-flavor result. |
| 901 | // First, extract the two fields. |
| 902 | const unsigned_result = try self.extractField(overflow_member_ty_id, op_result_id, 0); |
| 903 | const overflow = try self.extractField(overflow_member_ty_id, op_result_id, 0); |
| 904 | |
| 905 | // We need to convert the results to the types that Zig expects here. |
| 906 | // The `result` is the same type except unsigned, so we can just bitcast that. |
| 907 | const result = try self.bitcast(operand_ty_id, unsigned_result); |
| 908 | |
| 909 | // The overflow needs to be converted into whatever is used to represent it in Zig. |
| 910 | const casted_overflow = blk: { |
| 911 | const ov_ty = result_ty.tupleFields().types[1]; |
| 912 | const ov_ty_id = try self.resolveTypeId(ov_ty); |
| 913 | const result_id = self.spv.allocId(); |
| 914 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 915 | .id_result_type = ov_ty_id, |
| 916 | .id_result = result_id, |
| 917 | .unsigned_value = overflow, |
| 918 | }); |
| 919 | break :blk result_id.toRef(); |
| 920 | }; |
| 921 | |
| 922 | // TODO: If copying this function for borrow, make sure to convert -1 to 1 as appropriate. |
| 923 | |
| 924 | // Finally, construct the Zig type. |
| 925 | // Layout is result, overflow. |
| 926 | const result_id = self.spv.allocId(); |
| 927 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| 928 | .id_result_type = result_type_id, |
| 929 | .id_result = result_id, |
| 930 | .constituents = &.{ |
| 931 | result, |
| 932 | casted_overflow, |
| 933 | }, |
| 934 | }); |
| 935 | return result_id.toRef(); |
| 936 | } |
| 937 | |
| 844 | 938 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 845 | 939 | if (self.liveness.isUnused(inst)) return null; |
| 846 | 940 | const ty = self.air.typeOfIndex(inst); |
| ... | ... | @@ -923,20 +1017,24 @@ pub const DeclGen = struct { |
| 923 | 1017 | return result_id.toRef(); |
| 924 | 1018 | } |
| 925 | 1019 | |
| 926 | | fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 927 | | if (self.liveness.isUnused(inst)) return null; |
| 928 | | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 929 | | const operand_id = try self.resolve(ty_op.operand); |
| 1020 | fn bitcast(self: *DeclGen, target_type_id: IdResultType, value_id: IdRef) !IdRef { |
| 930 | 1021 | const result_id = self.spv.allocId(); |
| 931 | | const result_type_id = try self.resolveTypeId(self.air.typeOfIndex(inst)); |
| 932 | 1022 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 933 | | .id_result_type = result_type_id, |
| 1023 | .id_result_type = target_type_id, |
| 934 | 1024 | .id_result = result_id, |
| 935 | | .operand = operand_id, |
| 1025 | .operand = value_id, |
| 936 | 1026 | }); |
| 937 | 1027 | return result_id.toRef(); |
| 938 | 1028 | } |
| 939 | 1029 | |
| 1030 | fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 1031 | if (self.liveness.isUnused(inst)) return null; |
| 1032 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1033 | const operand_id = try self.resolve(ty_op.operand); |
| 1034 | const result_type_id = try self.resolveTypeId(self.air.typeOfIndex(inst)); |
| 1035 | return try self.bitcast(result_type_id, operand_id); |
| 1036 | } |
| 1037 | |
| 940 | 1038 | fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 941 | 1039 | if (self.liveness.isUnused(inst)) return null; |
| 942 | 1040 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | ... | @@ -951,19 +1049,27 @@ pub const DeclGen = struct { |
| 951 | 1049 | return result_id.toRef(); |
| 952 | 1050 | } |
| 953 | 1051 | |
| 954 | | fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef { |
| 955 | | if (self.liveness.isUnused(inst)) return null; |
| 956 | | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1052 | fn extractField(self: *DeclGen, result_ty: IdResultType, object: IdRef, field: u32) !IdRef { |
| 957 | 1053 | const result_id = self.spv.allocId(); |
| 958 | 1054 | try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ |
| 959 | | .id_result_type = try self.resolveTypeId(self.air.typeOfIndex(inst)), |
| 1055 | .id_result_type = result_ty, |
| 960 | 1056 | .id_result = result_id, |
| 961 | | .composite = try self.resolve(ty_op.operand), |
| 1057 | .composite = object, |
| 962 | 1058 | .indexes = &.{field}, |
| 963 | 1059 | }); |
| 964 | 1060 | return result_id.toRef(); |
| 965 | 1061 | } |
| 966 | 1062 | |
| 1063 | fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef { |
| 1064 | if (self.liveness.isUnused(inst)) return null; |
| 1065 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1066 | return try self.extractField( |
| 1067 | try self.resolveTypeId(self.air.typeOfIndex(inst)), |
| 1068 | try self.resolve(ty_op.operand), |
| 1069 | field, |
| 1070 | ); |
| 1071 | } |
| 1072 | |
| 967 | 1073 | fn airSliceElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 968 | 1074 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 969 | 1075 | const slice_ty = self.air.typeOf(bin_op.lhs); |