authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2022-11-26 23:42:04+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-09 01:51:49+02:00
log6146abee1ef19f1c5380d0afba6f38895ce81e0b
treedeca9953b420b50f9d64621dd8bf6d419868b3dc
parentc23d668c79e25ba6a83ec01d9e58f136a5afe7cf
signaturelock-open Commit is signed but in an unrecognized format.

spirv: add_with_overflow

Implements lowering for the add_with_overflow AIR instructions. Also implements a helper function, simpleStructType, to quickly generate a SPIR-V structure type without having to do the whole allocation dance.

2 files changed, 144 insertions(+), 38 deletions(-)

src/codegen/spirv.zig+141-35
...@@ -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 }
461461
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);
557573
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 operations580 // 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 }
599617
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 }
617634
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),
711726
727 .add_with_overflow => try self.airOverflowArithOp(inst),
728
712 .shuffle => try self.airShuffle(inst),729 .shuffle => try self.airShuffle(inst),
713730
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 {
719736
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 }
843861
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 }
9251019
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 }
9391029
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 }
9531051
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 }
9661062
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);
src/codegen/spirv/type.zig+3-3
...@@ -436,17 +436,17 @@ pub const Type = extern union {...@@ -436,17 +436,17 @@ pub const Type = extern union {
436 base: Payload = .{ .tag = .@"struct" },436 base: Payload = .{ .tag = .@"struct" },
437 // TODO: name437 // TODO: name
438 members: []Member,438 members: []Member,
439 decorations: StructDecorations,439 decorations: StructDecorations = .{},
440440
441 /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by441 /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by
442 /// order of the `members` slice and `MemberDecorations` struct.442 /// order of the `members` slice and `MemberDecorations` struct.
443 member_decoration_extra: []u32,443 member_decoration_extra: []u32 = &.{},
444444
445 pub const Member = struct {445 pub const Member = struct {
446 ty: Ref,446 ty: Ref,
447 offset: u32,447 offset: u32,
448 // TODO: name448 // TODO: name
449 decorations: MemberDecorations,449 decorations: MemberDecorations = .{},
450 };450 };
451451
452 pub const StructDecorations = packed struct {452 pub const StructDecorations = packed struct {