| ... | @@ -824,9 +824,21 @@ pub const DeclGen = struct { | ... | @@ -824,9 +824,21 @@ pub const DeclGen = struct { |
| 824 | const air_tags = self.air.instructions.items(.tag); | 824 | const air_tags = self.air.instructions.items(.tag); |
| 825 | const maybe_result_id: ?IdRef = switch (air_tags[inst]) { | 825 | const maybe_result_id: ?IdRef = switch (air_tags[inst]) { |
| 826 | // zig fmt: off | 826 | // zig fmt: off |
| 827 | .add, .addwrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd), | 827 | .add, .addwrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd, true), |
| 828 | .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub), | 828 | .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub, true), |
| 829 | .mul, .mulwrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul), | 829 | .mul, .mulwrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul, true), |
| | 830 | |
| | 831 | .div_float, |
| | 832 | .div_float_optimized, |
| | 833 | // TODO: Check that this is the right operation. |
| | 834 | .div_trunc, |
| | 835 | .div_trunc_optimized, |
| | 836 | => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv, false), |
| | 837 | // TODO: Check if this is the right operation |
| | 838 | // TODO: Make airArithOp for rem not emit a mask for the LHS. |
| | 839 | .rem, |
| | 840 | .rem_optimized, |
| | 841 | => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem, false), |
| 830 | | 842 | |
| 831 | .add_with_overflow => try self.airOverflowArithOp(inst), | 843 | .add_with_overflow => try self.airOverflowArithOp(inst), |
| 832 | | 844 | |
| ... | @@ -838,8 +850,9 @@ pub const DeclGen = struct { | ... | @@ -838,8 +850,9 @@ pub const DeclGen = struct { |
| 838 | .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd), | 850 | .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd), |
| 839 | .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr), | 851 | .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr), |
| 840 | | 852 | |
| 841 | .bitcast => try self.airBitcast(inst), | 853 | .bitcast => try self.airBitcast(inst), |
| 842 | .not => try self.airNot(inst), | 854 | .intcast => try self.airIntcast(inst), |
| | 855 | .not => try self.airNot(inst), |
| 843 | | 856 | |
| 844 | .slice_ptr => try self.airSliceField(inst, 0), | 857 | .slice_ptr => try self.airSliceField(inst, 0), |
| 845 | .slice_len => try self.airSliceField(inst, 1), | 858 | .slice_len => try self.airSliceField(inst, 1), |
| ... | @@ -909,20 +922,47 @@ pub const DeclGen = struct { | ... | @@ -909,20 +922,47 @@ pub const DeclGen = struct { |
| 909 | return result_id.toRef(); | 922 | return result_id.toRef(); |
| 910 | } | 923 | } |
| 911 | | 924 | |
| | 925 | fn maskStrangeInt(self: *DeclGen, ty_id: IdResultType, int_id: IdRef, bits: u16) !IdRef { |
| | 926 | const backing_bits = self.backingIntBits(bits).?; |
| | 927 | const mask_value = if (bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @intCast(u6, bits)) - 1; |
| | 928 | const mask_lit: spec.LiteralContextDependentNumber = switch (backing_bits) { |
| | 929 | 1...32 => .{ .uint32 = @truncate(u32, mask_value) }, |
| | 930 | 33...64 => .{ .uint64 = mask_value }, |
| | 931 | else => unreachable, |
| | 932 | }; |
| | 933 | // TODO: We should probably optimize these constants a bit. |
| | 934 | const mask_id = self.spv.allocId(); |
| | 935 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstant, .{ |
| | 936 | .id_result_type = ty_id, |
| | 937 | .id_result = mask_id, |
| | 938 | .value = mask_lit, |
| | 939 | }); |
| | 940 | const result_id = self.spv.allocId(); |
| | 941 | try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{ |
| | 942 | .id_result_type = ty_id, |
| | 943 | .id_result = result_id, |
| | 944 | .operand_1 = int_id, |
| | 945 | .operand_2 = mask_id.toRef(), |
| | 946 | }); |
| | 947 | return result_id.toRef(); |
| | 948 | } |
| | 949 | |
| 912 | fn airArithOp( | 950 | fn airArithOp( |
| 913 | self: *DeclGen, | 951 | self: *DeclGen, |
| 914 | inst: Air.Inst.Index, | 952 | inst: Air.Inst.Index, |
| 915 | comptime fop: Opcode, | 953 | comptime fop: Opcode, |
| 916 | comptime sop: Opcode, | 954 | comptime sop: Opcode, |
| 917 | comptime uop: Opcode, | 955 | comptime uop: Opcode, |
| | 956 | /// true if this operation holds under modular arithmetic. |
| | 957 | comptime modular: bool, |
| 918 | ) !?IdRef { | 958 | ) !?IdRef { |
| 919 | if (self.liveness.isUnused(inst)) return null; | 959 | if (self.liveness.isUnused(inst)) return null; |
| 920 | // LHS and RHS are guaranteed to have the same type, and AIR guarantees | 960 | // LHS and RHS are guaranteed to have the same type, and AIR guarantees |
| 921 | // the result to be the same as the LHS and RHS, which matches SPIR-V. | 961 | // the result to be the same as the LHS and RHS, which matches SPIR-V. |
| 922 | const ty = self.air.typeOfIndex(inst); | 962 | const ty = self.air.typeOfIndex(inst); |
| 923 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 963 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 924 | const lhs_id = try self.resolve(bin_op.lhs); | 964 | var lhs_id = try self.resolve(bin_op.lhs); |
| 925 | const rhs_id = try self.resolve(bin_op.rhs); | 965 | var rhs_id = try self.resolve(bin_op.rhs); |
| 926 | | 966 | |
| 927 | const result_id = self.spv.allocId(); | 967 | const result_id = self.spv.allocId(); |
| 928 | const result_type_id = try self.resolveTypeId(ty); | 968 | const result_type_id = try self.resolveTypeId(ty); |
| ... | @@ -938,15 +978,22 @@ pub const DeclGen = struct { | ... | @@ -938,15 +978,22 @@ pub const DeclGen = struct { |
| 938 | .composite_integer => { | 978 | .composite_integer => { |
| 939 | return self.todo("binary operations for composite integers", .{}); | 979 | return self.todo("binary operations for composite integers", .{}); |
| 940 | }, | 980 | }, |
| 941 | .strange_integer => { | 981 | .strange_integer => blk: { |
| 942 | return self.todo("binary operations for strange integers", .{}); | 982 | if (!modular) { |
| | 983 | lhs_id = try self.maskStrangeInt(result_type_id, lhs_id, info.bits); |
| | 984 | rhs_id = try self.maskStrangeInt(result_type_id, rhs_id, info.bits); |
| | 985 | } |
| | 986 | break :blk switch (info.signedness) { |
| | 987 | .signed => @as(usize, 1), |
| | 988 | .unsigned => @as(usize, 2), |
| | 989 | }; |
| 943 | }, | 990 | }, |
| 944 | .integer => switch (info.signedness) { | 991 | .integer => switch (info.signedness) { |
| 945 | .signed => @as(usize, 1), | 992 | .signed => @as(usize, 1), |
| 946 | .unsigned => @as(usize, 2), | 993 | .unsigned => @as(usize, 2), |
| 947 | }, | 994 | }, |
| 948 | .float => 0, | 995 | .float => 0, |
| 949 | else => unreachable, | 996 | .bool => unreachable, |
| 950 | }; | 997 | }; |
| 951 | | 998 | |
| 952 | const operands = .{ | 999 | const operands = .{ |
| ... | @@ -981,11 +1028,18 @@ pub const DeclGen = struct { | ... | @@ -981,11 +1028,18 @@ pub const DeclGen = struct { |
| 981 | const operand_ty = self.air.typeOf(extra.lhs); | 1028 | const operand_ty = self.air.typeOf(extra.lhs); |
| 982 | const result_ty = self.air.typeOfIndex(inst); | 1029 | const result_ty = self.air.typeOfIndex(inst); |
| 983 | | 1030 | |
| | 1031 | const info = try self.arithmeticTypeInfo(operand_ty); |
| | 1032 | switch (info.class) { |
| | 1033 | .composite_integer => return self.todo("overflow ops for composite integers", .{}), |
| | 1034 | .strange_integer => return self.todo("overflow ops for strange integers", .{}), |
| | 1035 | .integer => {}, |
| | 1036 | .float, .bool => unreachable, |
| | 1037 | } |
| | 1038 | |
| 984 | const operand_ty_id = try self.resolveTypeId(operand_ty); | 1039 | const operand_ty_id = try self.resolveTypeId(operand_ty); |
| 985 | const result_type_id = try self.resolveTypeId(result_ty); | 1040 | const result_type_id = try self.resolveTypeId(result_ty); |
| 986 | | 1041 | |
| 987 | const operand_bits = operand_ty.intInfo(target).bits; | 1042 | const overflow_member_ty = try self.intType(.unsigned, info.bits); |
| 988 | const overflow_member_ty = try self.intType(.unsigned, operand_bits); | | |
| 989 | const overflow_member_ty_id = self.spv.typeResultId(overflow_member_ty); | 1043 | const overflow_member_ty_id = self.spv.typeResultId(overflow_member_ty); |
| 990 | | 1044 | |
| 991 | const op_result_id = blk: { | 1045 | const op_result_id = blk: { |
| ... | @@ -1083,8 +1137,8 @@ pub const DeclGen = struct { | ... | @@ -1083,8 +1137,8 @@ pub const DeclGen = struct { |
| 1083 | fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !?IdRef { | 1137 | fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !?IdRef { |
| 1084 | if (self.liveness.isUnused(inst)) return null; | 1138 | if (self.liveness.isUnused(inst)) return null; |
| 1085 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1139 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1086 | const lhs_id = try self.resolve(bin_op.lhs); | 1140 | var lhs_id = try self.resolve(bin_op.lhs); |
| 1087 | const rhs_id = try self.resolve(bin_op.rhs); | 1141 | var rhs_id = try self.resolve(bin_op.rhs); |
| 1088 | const result_id = self.spv.allocId(); | 1142 | const result_id = self.spv.allocId(); |
| 1089 | const result_type_id = try self.resolveTypeId(Type.initTag(.bool)); | 1143 | const result_type_id = try self.resolveTypeId(Type.initTag(.bool)); |
| 1090 | const op_ty = self.air.typeOf(bin_op.lhs); | 1144 | const op_ty = self.air.typeOf(bin_op.lhs); |
| ... | @@ -1100,10 +1154,15 @@ pub const DeclGen = struct { | ... | @@ -1100,10 +1154,15 @@ pub const DeclGen = struct { |
| 1100 | }, | 1154 | }, |
| 1101 | .float => 0, | 1155 | .float => 0, |
| 1102 | .bool => 1, | 1156 | .bool => 1, |
| 1103 | // TODO: Should strange integers be masked before comparison? | 1157 | .strange_integer => blk: { |
| 1104 | .strange_integer, | 1158 | lhs_id = try self.maskStrangeInt(result_type_id, lhs_id, info.bits); |
| 1105 | .integer, | 1159 | rhs_id = try self.maskStrangeInt(result_type_id, rhs_id, info.bits); |
| 1106 | => switch (info.signedness) { | 1160 | break :blk switch (info.signedness) { |
| | 1161 | .signed => @as(usize, 1), |
| | 1162 | .unsigned => @as(usize, 2), |
| | 1163 | }; |
| | 1164 | }, |
| | 1165 | .integer => switch (info.signedness) { |
| 1107 | .signed => @as(usize, 1), | 1166 | .signed => @as(usize, 1), |
| 1108 | .unsigned => @as(usize, 2), | 1167 | .unsigned => @as(usize, 2), |
| 1109 | }, | 1168 | }, |
| ... | @@ -1144,6 +1203,31 @@ pub const DeclGen = struct { | ... | @@ -1144,6 +1203,31 @@ pub const DeclGen = struct { |
| 1144 | return try self.bitcast(result_type_id, operand_id); | 1203 | return try self.bitcast(result_type_id, operand_id); |
| 1145 | } | 1204 | } |
| 1146 | | 1205 | |
| | 1206 | fn airIntcast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| | 1207 | if (self.liveness.isUnused(inst)) return null; |
| | 1208 | |
| | 1209 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 1210 | const operand_id = try self.resolve(ty_op.operand); |
| | 1211 | const dest_ty = self.air.typeOfIndex(inst); |
| | 1212 | const dest_info = try self.arithmeticTypeInfo(dest_ty); |
| | 1213 | const dest_ty_id = try self.resolveTypeId(dest_ty); |
| | 1214 | |
| | 1215 | const result_id = self.spv.allocId(); |
| | 1216 | switch (dest_info.signedness) { |
| | 1217 | .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ |
| | 1218 | .id_result_type = dest_ty_id, |
| | 1219 | .id_result = result_id, |
| | 1220 | .signed_value = operand_id, |
| | 1221 | }), |
| | 1222 | .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| | 1223 | .id_result_type = dest_ty_id, |
| | 1224 | .id_result = result_id, |
| | 1225 | .unsigned_value = operand_id, |
| | 1226 | }), |
| | 1227 | } |
| | 1228 | return result_id.toRef(); |
| | 1229 | } |
| | 1230 | |
| 1147 | fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 1231 | fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 1148 | if (self.liveness.isUnused(inst)) return null; | 1232 | if (self.liveness.isUnused(inst)) return null; |
| 1149 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1233 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |