| ... | @@ -459,6 +459,22 @@ pub const DeclGen = struct { | ... | @@ -459,6 +459,22 @@ pub const DeclGen = struct { |
| 459 | return try self.intType(.unsigned, self.getTarget().cpu.arch.ptrBitWidth()); | 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 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. | 478 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. |
| 463 | fn resolveType(self: *DeclGen, ty: Type) Error!SpvType.Ref { | 479 | fn resolveType(self: *DeclGen, ty: Type) Error!SpvType.Ref { |
| 464 | const target = self.getTarget(); | 480 | const target = self.getTarget(); |
| ... | @@ -555,25 +571,10 @@ pub const DeclGen = struct { | ... | @@ -555,25 +571,10 @@ pub const DeclGen = struct { |
| 555 | const len_align = len_ty.abiAlignment(target); | 571 | const len_align = len_ty.abiAlignment(target); |
| 556 | const len_offset = std.mem.alignForwardGeneric(u64, ptr_size, len_align); | 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); | 574 | return try self.simpleStructType(&.{ |
| 559 | members[0] = .{ | 575 | .{ .ty = spv_ptr_ty, .offset = 0 }, |
| 560 | .ty = spv_ptr_ty, | 576 | .{ .ty = try self.sizeType(), .offset = @intCast(u32, len_offset) }, |
| 561 | .offset = 0, | 577 | }); |
| 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)); | | |
| 577 | }, | 578 | }, |
| 578 | .Vector => { | 579 | .Vector => { |
| 579 | // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations | 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,7 +595,24 @@ pub const DeclGen = struct { |
| 594 | }, | 595 | }, |
| 595 | .Struct => { | 596 | .Struct => { |
| 596 | if (ty.isSimpleTupleOrAnonStruct()) { | 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 | const struct_ty = ty.castTag(.@"struct").?.data; | 618 | const struct_ty = ty.castTag(.@"struct").?.data; |
| ... | @@ -611,15 +629,12 @@ pub const DeclGen = struct { | ... | @@ -611,15 +629,12 @@ pub const DeclGen = struct { |
| 611 | members[member_index] = .{ | 629 | members[member_index] = .{ |
| 612 | .ty = try self.resolveType(field.ty), | 630 | .ty = try self.resolveType(field.ty), |
| 613 | .offset = field.offset, | 631 | .offset = field.offset, |
| 614 | .decorations = .{}, | | |
| 615 | }; | 632 | }; |
| 616 | } | 633 | } |
| 617 | | 634 | |
| 618 | const payload = try self.spv.arena.create(SpvType.Payload.Struct); | 635 | const payload = try self.spv.arena.create(SpvType.Payload.Struct); |
| 619 | payload.* = .{ | 636 | payload.* = .{ |
| 620 | .members = members[0..member_index], | 637 | .members = members[0..member_index], |
| 621 | .decorations = .{}, | | |
| 622 | .member_decoration_extra = &.{}, | | |
| 623 | }; | 638 | }; |
| 624 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); | 639 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); |
| 625 | }, | 640 | }, |
| ... | @@ -709,6 +724,8 @@ pub const DeclGen = struct { | ... | @@ -709,6 +724,8 @@ pub const DeclGen = struct { |
| 709 | .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub), | 724 | .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub), |
| 710 | .mul, .mulwrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul), | 725 | .mul, .mulwrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul), |
| 711 | | 726 | |
| | 727 | .add_with_overflow => try self.airOverflowArithOp(inst), |
| | 728 | |
| 712 | .shuffle => try self.airShuffle(inst), | 729 | .shuffle => try self.airShuffle(inst), |
| 713 | | 730 | |
| 714 | .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), | 731 | .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), |
| ... | @@ -719,6 +736,7 @@ pub const DeclGen = struct { | ... | @@ -719,6 +736,7 @@ pub const DeclGen = struct { |
| 719 | | 736 | |
| 720 | .bitcast => try self.airBitcast(inst), | 737 | .bitcast => try self.airBitcast(inst), |
| 721 | .not => try self.airNot(inst), | 738 | .not => try self.airNot(inst), |
| | 739 | |
| 722 | .slice_ptr => try self.airSliceField(inst, 0), | 740 | .slice_ptr => try self.airSliceField(inst, 0), |
| 723 | .slice_len => try self.airSliceField(inst, 1), | 741 | .slice_len => try self.airSliceField(inst, 1), |
| 724 | .slice_elem_ptr => try self.airSliceElemPtr(inst), | 742 | .slice_elem_ptr => try self.airSliceElemPtr(inst), |
| ... | @@ -841,6 +859,82 @@ pub const DeclGen = struct { | ... | @@ -841,6 +859,82 @@ pub const DeclGen = struct { |
| 841 | return result_id.toRef(); | 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 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 938 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 845 | if (self.liveness.isUnused(inst)) return null; | 939 | if (self.liveness.isUnused(inst)) return null; |
| 846 | const ty = self.air.typeOfIndex(inst); | 940 | const ty = self.air.typeOfIndex(inst); |
| ... | @@ -923,20 +1017,24 @@ pub const DeclGen = struct { | ... | @@ -923,20 +1017,24 @@ pub const DeclGen = struct { |
| 923 | return result_id.toRef(); | 1017 | return result_id.toRef(); |
| 924 | } | 1018 | } |
| 925 | | 1019 | |
| 926 | fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 1020 | fn bitcast(self: *DeclGen, target_type_id: IdResultType, value_id: IdRef) !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); | | |
| 930 | const result_id = self.spv.allocId(); | 1021 | const result_id = self.spv.allocId(); |
| 931 | const result_type_id = try self.resolveTypeId(self.air.typeOfIndex(inst)); | | |
| 932 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ | 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 | .id_result = result_id, | 1024 | .id_result = result_id, |
| 935 | .operand = operand_id, | 1025 | .operand = value_id, |
| 936 | }); | 1026 | }); |
| 937 | return result_id.toRef(); | 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 | fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 1038 | fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 941 | if (self.liveness.isUnused(inst)) return null; | 1039 | if (self.liveness.isUnused(inst)) return null; |
| 942 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1040 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | @@ -951,19 +1049,27 @@ pub const DeclGen = struct { | ... | @@ -951,19 +1049,27 @@ pub const DeclGen = struct { |
| 951 | return result_id.toRef(); | 1049 | return result_id.toRef(); |
| 952 | } | 1050 | } |
| 953 | | 1051 | |
| 954 | fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef { | 1052 | fn extractField(self: *DeclGen, result_ty: IdResultType, object: IdRef, field: u32) !IdRef { |
| 955 | if (self.liveness.isUnused(inst)) return null; | | |
| 956 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | | |
| 957 | const result_id = self.spv.allocId(); | 1053 | const result_id = self.spv.allocId(); |
| 958 | try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ | 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 | .id_result = result_id, | 1056 | .id_result = result_id, |
| 961 | .composite = try self.resolve(ty_op.operand), | 1057 | .composite = object, |
| 962 | .indexes = &.{field}, | 1058 | .indexes = &.{field}, |
| 963 | }); | 1059 | }); |
| 964 | return result_id.toRef(); | 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 | fn airSliceElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 1073 | fn airSliceElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 968 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1074 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 969 | const slice_ty = self.air.typeOf(bin_op.lhs); | 1075 | const slice_ty = self.air.typeOf(bin_op.lhs); |