authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-10-08 18:52:58+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-10-15 14:00:09+02:00
logb1499df1b88b0fa25c4d7e4a16cb1715db452227
tree1e14634e6f9ca40fd5d31a7ccc357c6f57e52169
parentdc44baf763f38ba3735d45fc4f633cac13949b0e
signaturebadge-check Signed by SSH key SHA256:CQ99aPxq+RueiL9u7z0FEki5Fm7V6T8q4PrEGmINrA4

spirv: sign-extension for strange integers


2 files changed, 58 insertions(+), 15 deletions(-)

src/codegen/spirv.zig+55-15
...@@ -270,6 +270,12 @@ const DeclGen = struct {...@@ -270,6 +270,12 @@ const DeclGen = struct {
270 /// This is the actual number of bits of the type, not the size of the backing integer.270 /// This is the actual number of bits of the type, not the size of the backing integer.
271 bits: u16,271 bits: u16,
272272
273 /// The number of bits required to store the type.
274 /// For `integer` and `float`, this is equal to `bits`.
275 /// For `strange_integer` and `bool` this is the size of the backing integer.
276 /// For `composite_integer` this is 0 (TODO)
277 backing_bits: u16,
278
273 /// Whether the type is a vector.279 /// Whether the type is a vector.
274 is_vector: bool,280 is_vector: bool,
275281
...@@ -499,12 +505,14 @@ const DeclGen = struct {...@@ -499,12 +505,14 @@ const DeclGen = struct {
499 return switch (ty.zigTypeTag(mod)) {505 return switch (ty.zigTypeTag(mod)) {
500 .Bool => ArithmeticTypeInfo{506 .Bool => ArithmeticTypeInfo{
501 .bits = 1, // Doesn't matter for this class.507 .bits = 1, // Doesn't matter for this class.
508 .backing_bits = self.backingIntBits(1).?,
502 .is_vector = false,509 .is_vector = false,
503 .signedness = .unsigned, // Technically, but doesn't matter for this class.510 .signedness = .unsigned, // Technically, but doesn't matter for this class.
504 .class = .bool,511 .class = .bool,
505 },512 },
506 .Float => ArithmeticTypeInfo{513 .Float => ArithmeticTypeInfo{
507 .bits = ty.floatBits(target),514 .bits = ty.floatBits(target),
515 .backing_bits = ty.floatBits(target), // TODO: F80?
508 .is_vector = false,516 .is_vector = false,
509 .signedness = .signed, // Technically, but doesn't matter for this class.517 .signedness = .signed, // Technically, but doesn't matter for this class.
510 .class = .float,518 .class = .float,
...@@ -515,6 +523,7 @@ const DeclGen = struct {...@@ -515,6 +523,7 @@ const DeclGen = struct {
515 const maybe_backing_bits = self.backingIntBits(int_info.bits);523 const maybe_backing_bits = self.backingIntBits(int_info.bits);
516 break :blk ArithmeticTypeInfo{524 break :blk ArithmeticTypeInfo{
517 .bits = int_info.bits,525 .bits = int_info.bits,
526 .backing_bits = maybe_backing_bits orelse 0,
518 .is_vector = false,527 .is_vector = false,
519 .signedness = int_info.signedness,528 .signedness = int_info.signedness,
520 .class = if (maybe_backing_bits) |backing_bits|529 .class = if (maybe_backing_bits) |backing_bits|
...@@ -2154,17 +2163,48 @@ const DeclGen = struct {...@@ -2154,17 +2163,48 @@ const DeclGen = struct {
2154 return result_id;2163 return result_id;
2155 }2164 }
21562165
2157 fn maskStrangeInt(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, bits: u16) !IdRef {2166 /// This function canonicalizes a "strange" integer value:
2158 const mask_value = if (bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(bits))) - 1;2167 /// For unsigned integers, the value is masked so that only the relevant bits can contain
2159 const result_id = self.spv.allocId();2168 /// non-zeros.
2160 const mask_id = try self.constInt(ty_ref, mask_value);2169 /// For signed integers, the value is also sign extended.
2161 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{2170 fn normalizeInt(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef {
2162 .id_result_type = self.typeId(ty_ref),2171 if (info.bits == info.backing_bits) {
2163 .id_result = result_id,2172 return value_id;
2164 .operand_1 = value_id,2173 }
2165 .operand_2 = mask_id,2174
2166 });2175 switch (info.signedness) {
2167 return result_id;2176 .unsigned => {
2177 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
2178 const result_id = self.spv.allocId();
2179 const mask_id = try self.constInt(ty_ref, mask_value);
2180 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{
2181 .id_result_type = self.typeId(ty_ref),
2182 .id_result = result_id,
2183 .operand_1 = value_id,
2184 .operand_2 = mask_id,
2185 });
2186 return result_id;
2187 },
2188 .signed => {
2189 // Shift left and right so that we can copy the sight bit that way.
2190 const shift_amt_id = try self.constInt(ty_ref, info.backing_bits - info.bits);
2191 const left_id = self.spv.allocId();
2192 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{
2193 .id_result_type = self.typeId(ty_ref),
2194 .id_result = left_id,
2195 .base = value_id,
2196 .shift = shift_amt_id,
2197 });
2198 const right_id = self.spv.allocId();
2199 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{
2200 .id_result_type = self.typeId(ty_ref),
2201 .id_result = right_id,
2202 .base = left_id,
2203 .shift = shift_amt_id,
2204 });
2205 return right_id;
2206 },
2207 }
2168 }2208 }
21692209
2170 fn airArithOp(2210 fn airArithOp(
...@@ -2199,8 +2239,8 @@ const DeclGen = struct {...@@ -2199,8 +2239,8 @@ const DeclGen = struct {
2199 },2239 },
2200 .strange_integer => blk: {2240 .strange_integer => blk: {
2201 if (!modular) {2241 if (!modular) {
2202 lhs_id = try self.maskStrangeInt(result_ty_ref, lhs_id, info.bits);2242 lhs_id = try self.normalizeInt(result_ty_ref, lhs_id, info);
2203 rhs_id = try self.maskStrangeInt(result_ty_ref, rhs_id, info.bits);2243 rhs_id = try self.normalizeInt(result_ty_ref, rhs_id, info);
2204 }2244 }
2205 break :blk switch (info.signedness) {2245 break :blk switch (info.signedness) {
2206 .signed => @as(usize, 1),2246 .signed => @as(usize, 1),
...@@ -2565,8 +2605,8 @@ const DeclGen = struct {...@@ -2565,8 +2605,8 @@ const DeclGen = struct {
2565 .strange_integer => sign: {2605 .strange_integer => sign: {
2566 const op_ty_ref = try self.resolveType(op_ty, .direct);2606 const op_ty_ref = try self.resolveType(op_ty, .direct);
2567 // Mask operands before performing comparison.2607 // Mask operands before performing comparison.
2568 cmp_lhs_id = try self.maskStrangeInt(op_ty_ref, cmp_lhs_id, info.bits);2608 cmp_lhs_id = try self.normalizeInt(op_ty_ref, cmp_lhs_id, info);
2569 cmp_rhs_id = try self.maskStrangeInt(op_ty_ref, cmp_rhs_id, info.bits);2609 cmp_rhs_id = try self.normalizeInt(op_ty_ref, cmp_rhs_id, info);
2570 break :sign info.signedness;2610 break :sign info.signedness;
2571 },2611 },
2572 .integer => info.signedness,2612 .integer => info.signedness,
test/behavior/math.zig+3
...@@ -468,6 +468,9 @@ fn testDivision() !void {...@@ -468,6 +468,9 @@ fn testDivision() !void {
468 try expect(mod(i32, -14, -12) == -2);468 try expect(mod(i32, -14, -12) == -2);
469 try expect(mod(i32, -2, -12) == -2);469 try expect(mod(i32, -2, -12) == -2);
470470
471 try expect(divTrunc(i20, 20, -5) == -4);
472 try expect(divTrunc(i20, -20, -4) == 5);
473
471 comptime {474 comptime {
472 try expect(475 try expect(
473 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,476 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,