| author | |
| committer | |
| log | f810faa828de0ac1704e2c87f9fd467af16b564b |
| tree | f27211eff53a21116febf028e5faae0ccbaa7c81 |
| parent | c5af4c75da81616eadce8cf866aac86fb9e4ba9d |
Co-authored-by: riverbl <94326797+riverbl@users.noreply.github.com>
Co-authored-by: Ratakor <ratakor@disroot.org>28 files changed, 822 insertions(+), 13 deletions(-)
doc/langref.html.in+20-4| ... | @@ -1370,7 +1370,8 @@ a /= b{#endsyntax#}</pre></td> | ... | @@ -1370,7 +1370,8 @@ a /= b{#endsyntax#}</pre></td> |
| 1370 | <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.</li> | 1370 | <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.</li> |
| 1371 | <li>Signed integer operands must be comptime-known and positive. In other cases, use | 1371 | <li>Signed integer operands must be comptime-known and positive. In other cases, use |
| 1372 | {#link|@divTrunc#}, | 1372 | {#link|@divTrunc#}, |
| 1373 | {#link|@divFloor#}, or | 1373 | {#link|@divFloor#}, |
| 1374 | {#link|@divCeil#}, or | ||
| 1374 | {#link|@divExact#} instead. | 1375 | {#link|@divExact#} instead. |
| 1375 | </li> | 1376 | </li> |
| 1376 | <li>Invokes {#link|Peer Type Resolution#} for the operands.</li> | 1377 | <li>Invokes {#link|Peer Type Resolution#} for the operands.</li> |
| ... | @@ -4735,7 +4736,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val | ... | @@ -4735,7 +4736,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 4735 | <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li> | 4736 | <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li> |
| 4736 | </ul> | 4737 | </ul> |
| 4737 | <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.</p> | 4738 | <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.</p> |
| 4738 | {#see_also|@divTrunc|@divFloor#} | 4739 | {#see_also|@divTrunc|@divFloor|@divCeil#} |
| 4739 | {#header_close#} | 4740 | {#header_close#} |
| 4740 | {#header_open|@divFloor#} | 4741 | {#header_open|@divFloor#} |
| 4741 | <pre>{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}</pre> | 4742 | <pre>{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}</pre> |
| ... | @@ -4749,7 +4750,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val | ... | @@ -4749,7 +4750,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 4749 | <li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li> | 4750 | <li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li> |
| 4750 | </ul> | 4751 | </ul> |
| 4751 | <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.</p> | 4752 | <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.</p> |
| 4752 | {#see_also|@divTrunc|@divExact#} | 4753 | {#see_also|@divTrunc|@divCeil|@divExact#} |
| 4753 | {#header_close#} | 4754 | {#header_close#} |
| 4754 | {#header_open|@divTrunc#} | 4755 | {#header_open|@divTrunc#} |
| 4755 | <pre>{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}</pre> | 4756 | <pre>{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}</pre> |
| ... | @@ -4763,7 +4764,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val | ... | @@ -4763,7 +4764,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 4763 | <li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li> | 4764 | <li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li> |
| 4764 | </ul> | 4765 | </ul> |
| 4765 | <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.</p> | 4766 | <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.</p> |
| 4766 | {#see_also|@divFloor|@divExact#} | 4767 | {#see_also|@divFloor|@divCeil|@divExact#} |
| 4768 | {#header_close#} | ||
| 4769 | {#header_open|@divCeil#} | ||
| 4770 | <pre>{#syntax#}@divCeil(numerator: T, denominator: T) T{#endsyntax#}</pre> | ||
| 4771 | <p> | ||
| 4772 | Ceiled division. Rounds toward positive infinity. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and | ||
| 4773 | {#syntax#}!(@typeInfo(T) == .int and T.is_signed and numerator == std.math.minInt(T) and denominator == -1){#endsyntax#}. | ||
| 4774 | </p> | ||
| 4775 | <ul> | ||
| 4776 | <li>{#syntax#}@divCeil(5, 3) == 2{#endsyntax#}</li> | ||
| 4777 | <li>{#syntax#}@divCeil(-5, 3) == -1{#endsyntax#}</li> | ||
| 4778 | </ul> | ||
| 4779 | <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divCeil{#endsyntax#}.</p> | ||
| 4780 | {#see_also|@divFloor|@divTrunc|@divExact#} | ||
| 4767 | {#header_close#} | 4781 | {#header_close#} |
| 4768 | 4782 | ||
| 4769 | {#header_open|@embedFile#} | 4783 | {#header_open|@embedFile#} |
| ... | @@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val | ... | @@ -6095,6 +6109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 6095 | <li>{#syntax#}/{#endsyntax#} (division)</li> | 6109 | <li>{#syntax#}/{#endsyntax#} (division)</li> |
| 6096 | <li>{#link|@divTrunc#} (division)</li> | 6110 | <li>{#link|@divTrunc#} (division)</li> |
| 6097 | <li>{#link|@divFloor#} (division)</li> | 6111 | <li>{#link|@divFloor#} (division)</li> |
| 6112 | <li>{#link|@divCeil#} (division)</li> | ||
| 6098 | <li>{#link|@divExact#} (division)</li> | 6113 | <li>{#link|@divExact#} (division)</li> |
| 6099 | </ul> | 6114 | </ul> |
| 6100 | <p>Example with addition at compile-time:</p> | 6115 | <p>Example with addition at compile-time:</p> |
| ... | @@ -6112,6 +6127,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val | ... | @@ -6112,6 +6127,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 6112 | <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li> | 6127 | <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li> |
| 6113 | <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li> | 6128 | <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li> |
| 6114 | <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li> | 6129 | <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li> |
| 6130 | <li>{#syntax#}@import("std").math.divCeil{#endsyntax#}</li> | ||
| 6115 | <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li> | 6131 | <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li> |
| 6116 | <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li> | 6132 | <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li> |
| 6117 | </ul> | 6133 | </ul> |
lib/std/math/big/int.zig+66| ... | @@ -1221,6 +1221,72 @@ pub const Mutable = struct { | ... | @@ -1221,6 +1221,72 @@ pub const Mutable = struct { |
| 1221 | } | 1221 | } |
| 1222 | } | 1222 | } |
| 1223 | 1223 | ||
| 1224 | /// q = a / b (rem r) | ||
| 1225 | /// | ||
| 1226 | /// a / b are ceiled (rounded towards +inf). | ||
| 1227 | /// q may alias with a or b. | ||
| 1228 | /// | ||
| 1229 | /// Asserts there is enough memory to store q and r. | ||
| 1230 | /// The upper bound for r limb count is `b.limbs.len`. | ||
| 1231 | /// The upper bound for q limb count is given by `a.limbs`. | ||
| 1232 | /// | ||
| 1233 | /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`. | ||
| 1234 | pub fn divCeil( | ||
| 1235 | q: *Mutable, | ||
| 1236 | r: *Mutable, | ||
| 1237 | a: Const, | ||
| 1238 | b: Const, | ||
| 1239 | limbs_buffer: []Limb, | ||
| 1240 | ) void { | ||
| 1241 | const sep = a.limbs.len + 2; | ||
| 1242 | var x = a.toMutable(limbs_buffer[0..sep]); | ||
| 1243 | var y = b.toMutable(limbs_buffer[sep..]); | ||
| 1244 | |||
| 1245 | // div performs truncating division (@divTrunc) which rounds towards negative | ||
| 1246 | // infinity if the result is positive and towards positive infinity if the result is | ||
| 1247 | // negative. | ||
| 1248 | div(q, r, &x, &y); | ||
| 1249 | |||
| 1250 | // @rem gives the remainder after @divTrunc, and is defined by: | ||
| 1251 | // x * @divTrunc(x, y) + @rem(x, y) = x | ||
| 1252 | // For all integers x, y with y != 0. | ||
| 1253 | // In the following comments, a, b will be integers with a >= 0, b > 0, and we will take | ||
| 1254 | // modCeil to be the remainder after @divCeil, defined by: | ||
| 1255 | // x * @divCeil(x, y) + modCeil(x, y) = x | ||
| 1256 | // For all integers x, y with y != 0. | ||
| 1257 | |||
| 1258 | if (a.positive != b.positive or r.eqlZero()) { | ||
| 1259 | // In this case either the result is negative or the remainder is 0. | ||
| 1260 | // If the result is negative then the default truncating division already rounds | ||
| 1261 | // towards positive infinity, so no adjustment is needed. | ||
| 1262 | // If the remainder is 0 then the division is exact and no adjustment is needed. | ||
| 1263 | } else if (a.positive) { | ||
| 1264 | // Both positive. | ||
| 1265 | // We have: | ||
| 1266 | // modCeil(a, b) != 0 | ||
| 1267 | // => @divCeil(a, b) = @divTrunc(a, b) + 1 | ||
| 1268 | // And: | ||
| 1269 | // b * @divTrunc(a, b) + @rem(a, b) = a | ||
| 1270 | // b * @divCeil(a, b) + modCeil(a, b) = a | ||
| 1271 | // => b * @divTrunc(a, b) + b + modCeil(a, b) = a | ||
| 1272 | // => modCeil(a, b) = @rem(a, b) - b | ||
| 1273 | q.addScalar(q.toConst(), 1); | ||
| 1274 | r.sub(r.toConst(), y.toConst()); | ||
| 1275 | } else { | ||
| 1276 | // Both negative. | ||
| 1277 | // We have: | ||
| 1278 | // modCeil(-a, -b) != 0 | ||
| 1279 | // => @divCeil(-a, -b) = @divTrunc(-a, -b) + 1 | ||
| 1280 | // And: | ||
| 1281 | // -b * @divTrunc(-a, -b) + @rem(-a, -b) = -a | ||
| 1282 | // -b * @divCeil(-a, -b) + modCeil(-a, -b) = -a | ||
| 1283 | // => -b * @divTrunc(-a, -b) - b + modCeil(-a, -b) = -a | ||
| 1284 | // => modCeil(-a, -b) = @rem(-a, -b) + b | ||
| 1285 | q.addScalar(q.toConst(), 1); | ||
| 1286 | r.add(r.toConst(), y.toConst().abs()); | ||
| 1287 | } | ||
| 1288 | } | ||
| 1289 | |||
| 1224 | /// q = a / b (rem r) | 1290 | /// q = a / b (rem r) |
| 1225 | /// | 1291 | /// |
| 1226 | /// a / b are truncated (rounded towards -inf). | 1292 | /// a / b are truncated (rounded towards -inf). |
lib/std/zig/AstGen.zig+2| ... | @@ -2845,6 +2845,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2845,6 +2845,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2845 | .bit_reverse, | 2845 | .bit_reverse, |
| 2846 | .div_exact, | 2846 | .div_exact, |
| 2847 | .div_floor, | 2847 | .div_floor, |
| 2848 | .div_ceil, | ||
| 2848 | .div_trunc, | 2849 | .div_trunc, |
| 2849 | .mod, | 2850 | .mod, |
| 2850 | .rem, | 2851 | .rem, |
| ... | @@ -9392,6 +9393,7 @@ fn builtinCall( | ... | @@ -9392,6 +9393,7 @@ fn builtinCall( |
| 9392 | 9393 | ||
| 9393 | .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact), | 9394 | .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact), |
| 9394 | .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor), | 9395 | .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor), |
| 9396 | .div_ceil => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_ceil), | ||
| 9395 | .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc), | 9397 | .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc), |
| 9396 | .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod), | 9398 | .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod), |
| 9397 | .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem), | 9399 | .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem), |
lib/std/zig/AstRlAnnotate.zig+1| ... | @@ -936,6 +936,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. | ... | @@ -936,6 +936,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. |
| 936 | }, | 936 | }, |
| 937 | .div_exact, | 937 | .div_exact, |
| 938 | .div_floor, | 938 | .div_floor, |
| 939 | .div_ceil, | ||
| 939 | .div_trunc, | 940 | .div_trunc, |
| 940 | .mod, | 941 | .mod, |
| 941 | .rem, | 942 | .rem, |
lib/std/zig/BuiltinFn.zig+8| ... | @@ -31,6 +31,7 @@ pub const Tag = enum { | ... | @@ -31,6 +31,7 @@ pub const Tag = enum { |
| 31 | c_va_copy, | 31 | c_va_copy, |
| 32 | c_va_end, | 32 | c_va_end, |
| 33 | c_va_start, | 33 | c_va_start, |
| 34 | div_ceil, | ||
| 34 | div_exact, | 35 | div_exact, |
| 35 | div_floor, | 36 | div_floor, |
| 36 | div_trunc, | 37 | div_trunc, |
| ... | @@ -398,6 +399,13 @@ pub const list = list: { | ... | @@ -398,6 +399,13 @@ pub const list = list: { |
| 398 | .param_count = 2, | 399 | .param_count = 2, |
| 399 | }, | 400 | }, |
| 400 | }, | 401 | }, |
| 402 | .{ | ||
| 403 | "@divCeil", | ||
| 404 | .{ | ||
| 405 | .tag = .div_ceil, | ||
| 406 | .param_count = 2, | ||
| 407 | }, | ||
| 408 | }, | ||
| 401 | .{ | 409 | .{ |
| 402 | "@divTrunc", | 410 | "@divTrunc", |
| 403 | .{ | 411 | .{ |
lib/std/zig/Zir.zig+7| ... | @@ -200,6 +200,9 @@ pub const Inst = struct { | ... | @@ -200,6 +200,9 @@ pub const Inst = struct { |
| 200 | /// Implements the `@divFloor` builtin. | 200 | /// Implements the `@divFloor` builtin. |
| 201 | /// Uses the `pl_node` union field with payload `Bin`. | 201 | /// Uses the `pl_node` union field with payload `Bin`. |
| 202 | div_floor, | 202 | div_floor, |
| 203 | /// Implements the `@divCeil` builtin. | ||
| 204 | /// Uses the `pl_node` union field with payload `Bin`. | ||
| 205 | div_ceil, | ||
| 203 | /// Implements the `@divTrunc` builtin. | 206 | /// Implements the `@divTrunc` builtin. |
| 204 | /// Uses the `pl_node` union field with payload `Bin`. | 207 | /// Uses the `pl_node` union field with payload `Bin`. |
| 205 | div_trunc, | 208 | div_trunc, |
| ... | @@ -1267,6 +1270,7 @@ pub const Inst = struct { | ... | @@ -1267,6 +1270,7 @@ pub const Inst = struct { |
| 1267 | .bit_reverse, | 1270 | .bit_reverse, |
| 1268 | .div_exact, | 1271 | .div_exact, |
| 1269 | .div_floor, | 1272 | .div_floor, |
| 1273 | .div_ceil, | ||
| 1270 | .div_trunc, | 1274 | .div_trunc, |
| 1271 | .mod, | 1275 | .mod, |
| 1272 | .rem, | 1276 | .rem, |
| ... | @@ -1547,6 +1551,7 @@ pub const Inst = struct { | ... | @@ -1547,6 +1551,7 @@ pub const Inst = struct { |
| 1547 | .bit_reverse, | 1551 | .bit_reverse, |
| 1548 | .div_exact, | 1552 | .div_exact, |
| 1549 | .div_floor, | 1553 | .div_floor, |
| 1554 | .div_ceil, | ||
| 1550 | .div_trunc, | 1555 | .div_trunc, |
| 1551 | .mod, | 1556 | .mod, |
| 1552 | .rem, | 1557 | .rem, |
| ... | @@ -1815,6 +1820,7 @@ pub const Inst = struct { | ... | @@ -1815,6 +1820,7 @@ pub const Inst = struct { |
| 1815 | 1820 | ||
| 1816 | .div_exact = .pl_node, | 1821 | .div_exact = .pl_node, |
| 1817 | .div_floor = .pl_node, | 1822 | .div_floor = .pl_node, |
| 1823 | .div_ceil = .pl_node, | ||
| 1818 | .div_trunc = .pl_node, | 1824 | .div_trunc = .pl_node, |
| 1819 | .mod = .pl_node, | 1825 | .mod = .pl_node, |
| 1820 | .rem = .pl_node, | 1826 | .rem = .pl_node, |
| ... | @@ -4115,6 +4121,7 @@ fn findTrackableInner( | ... | @@ -4115,6 +4121,7 @@ fn findTrackableInner( |
| 4115 | .mul_sat, | 4121 | .mul_sat, |
| 4116 | .div_exact, | 4122 | .div_exact, |
| 4117 | .div_floor, | 4123 | .div_floor, |
| 4124 | .div_ceil, | ||
| 4118 | .div_trunc, | 4125 | .div_trunc, |
| 4119 | .mod, | 4126 | .mod, |
| 4120 | .rem, | 4127 | .rem, |
lib/zig.h+32| ... | @@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t; | ... | @@ -813,6 +813,15 @@ typedef ptrdiff_t intptr_t; |
| 813 | static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ | 813 | static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ |
| 814 | return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \ | 814 | return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \ |
| 815 | } \ | 815 | } \ |
| 816 | \ | ||
| 817 | static inline uint##w##_t zig_div_ceil_u##w(uint##w##_t lhs, uint##w##_t rhs) { \ | ||
| 818 | return lhs / rhs + (lhs % rhs != UINT##w##_C(0) ? UINT##w##_C(1) : UINT##w##_C(0)); \ | ||
| 819 | } \ | ||
| 820 | \ | ||
| 821 | static inline int##w##_t zig_div_ceil_i##w(int##w##_t lhs, int##w##_t rhs) { \ | ||
| 822 | return lhs / rhs + (lhs % rhs != INT##w##_C(0) \ | ||
| 823 | ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) + INT##w##_C(1) : INT##w##_C(0)); \ | ||
| 824 | } \ | ||
| 816 | \ | 825 | \ |
| 817 | zig_basic_operator(uint##w##_t, mod_u##w, %) \ | 826 | zig_basic_operator(uint##w##_t, mod_u##w, %) \ |
| 818 | \ | 827 | \ |
| ... | @@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -2058,6 +2067,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { |
| 2058 | return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask)); | 2067 | return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask)); |
| 2059 | } | 2068 | } |
| 2060 | 2069 | ||
| 2070 | static inline zig_u128 zig_div_ceil_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 2071 | zig_u128 rem = zig_rem_u128(lhs, rhs); | ||
| 2072 | uint64_t mask = zig_or_u64(zig_hi_u128(rem), zig_lo_u128(rem)) != UINT64_C(0) | ||
| 2073 | ? UINT64_C(1) : UINT64_C(0); | ||
| 2074 | return zig_add_u128(zig_div_trunc_u128(lhs, rhs), zig_make_u128(UINT64_C(0), mask)); | ||
| 2075 | } | ||
| 2076 | |||
| 2077 | static inline zig_i128 zig_div_ceil_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 2078 | zig_i128 rem = zig_rem_i128(lhs, rhs); | ||
| 2079 | int64_t mask = zig_or_u64((uint64_t)zig_hi_i128(rem), zig_lo_i128(rem)) != UINT64_C(0) | ||
| 2080 | ? zig_shr_i64(zig_xor_i64(zig_hi_i128(lhs), zig_hi_i128(rhs)), UINT8_C(63)) + INT64_C(1) | ||
| 2081 | : INT64_C(0); | ||
| 2082 | return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(INT64_C(0), (uint64_t)mask)); | ||
| 2083 | } | ||
| 2084 | |||
| 2061 | #define zig_mod_u128 zig_rem_u128 | 2085 | #define zig_mod_u128 zig_rem_u128 |
| 2062 | 2086 | ||
| 2063 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { | 2087 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { |
| ... | @@ -3251,6 +3275,10 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs | ... | @@ -3251,6 +3275,10 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs |
| 3251 | zig_trap(); | 3275 | zig_trap(); |
| 3252 | } | 3276 | } |
| 3253 | 3277 | ||
| 3278 | static inline void zig_div_ceil_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { | ||
| 3279 | zig_trap(); | ||
| 3280 | } | ||
| 3281 | |||
| 3254 | zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); | 3282 | zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); |
| 3255 | static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { | 3283 | static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { |
| 3256 | if (!is_signed) { | 3284 | if (!is_signed) { |
| ... | @@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0))) | ... | @@ -4010,6 +4038,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0))) |
| 4010 | static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \ | 4038 | static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \ |
| 4011 | return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \ | 4039 | return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \ |
| 4012 | } \ | 4040 | } \ |
| 4041 | \ | ||
| 4042 | static inline zig_f##w zig_div_ceil_f##w(zig_f##w lhs, zig_f##w rhs) { \ | ||
| 4043 | return zig_ceil_f##w(zig_div_f##w(lhs, rhs)); \ | ||
| 4044 | } \ | ||
| 4013 | \ | 4045 | \ |
| 4014 | static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \ | 4046 | static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \ |
| 4015 | return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \ | 4047 | return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \ |
src/Air.zig+11| ... | @@ -143,6 +143,13 @@ pub const Inst = struct { | ... | @@ -143,6 +143,13 @@ pub const Inst = struct { |
| 143 | div_floor, | 143 | div_floor, |
| 144 | /// Same as `div_floor` with optimized float mode. | 144 | /// Same as `div_floor` with optimized float mode. |
| 145 | div_floor_optimized, | 145 | div_floor_optimized, |
| 146 | /// Ceiling integer or float division. For integers, wrapping is illegal behavior. | ||
| 147 | /// Both operands are guaranteed to be the same type, and the result type | ||
| 148 | /// is the same as both operands. | ||
| 149 | /// Uses the `bin_op` field. | ||
| 150 | div_ceil, | ||
| 151 | /// Same as `div_ceil` with optimized float mode. | ||
| 152 | div_ceil_optimized, | ||
| 146 | /// Integer or float division. | 153 | /// Integer or float division. |
| 147 | /// If a remainder would be produced, illegal behavior occurs. | 154 | /// If a remainder would be produced, illegal behavior occurs. |
| 148 | /// For integers, overflow is illegal behavior. | 155 | /// For integers, overflow is illegal behavior. |
| ... | @@ -1605,6 +1612,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) | ... | @@ -1605,6 +1612,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) |
| 1605 | .div_float, | 1612 | .div_float, |
| 1606 | .div_trunc, | 1613 | .div_trunc, |
| 1607 | .div_floor, | 1614 | .div_floor, |
| 1615 | .div_ceil, | ||
| 1608 | .div_exact, | 1616 | .div_exact, |
| 1609 | .rem, | 1617 | .rem, |
| 1610 | .mod, | 1618 | .mod, |
| ... | @@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) | ... | @@ -1624,6 +1632,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) |
| 1624 | .div_float_optimized, | 1632 | .div_float_optimized, |
| 1625 | .div_trunc_optimized, | 1633 | .div_trunc_optimized, |
| 1626 | .div_floor_optimized, | 1634 | .div_floor_optimized, |
| 1635 | .div_ceil_optimized, | ||
| 1627 | .div_exact_optimized, | 1636 | .div_exact_optimized, |
| 1628 | .rem_optimized, | 1637 | .rem_optimized, |
| 1629 | .mod_optimized, | 1638 | .mod_optimized, |
| ... | @@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { | ... | @@ -1985,6 +1994,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { |
| 1985 | .div_trunc_optimized, | 1994 | .div_trunc_optimized, |
| 1986 | .div_floor, | 1995 | .div_floor, |
| 1987 | .div_floor_optimized, | 1996 | .div_floor_optimized, |
| 1997 | .div_ceil, | ||
| 1998 | .div_ceil_optimized, | ||
| 1988 | .div_exact, | 1999 | .div_exact, |
| 1989 | .div_exact_optimized, | 2000 | .div_exact_optimized, |
| 1990 | .rem, | 2001 | .rem, |
src/Air/Legalize.zig+193-2| ... | @@ -54,6 +54,8 @@ pub const Feature = enum { | ... | @@ -54,6 +54,8 @@ pub const Feature = enum { |
| 54 | scalarize_div_trunc_optimized, | 54 | scalarize_div_trunc_optimized, |
| 55 | scalarize_div_floor, | 55 | scalarize_div_floor, |
| 56 | scalarize_div_floor_optimized, | 56 | scalarize_div_floor_optimized, |
| 57 | scalarize_div_ceil, | ||
| 58 | scalarize_div_ceil_optimized, | ||
| 57 | scalarize_div_exact, | 59 | scalarize_div_exact, |
| 58 | scalarize_div_exact_optimized, | 60 | scalarize_div_exact_optimized, |
| 59 | scalarize_rem, | 61 | scalarize_rem, |
| ... | @@ -173,6 +175,15 @@ pub const Feature = enum { | ... | @@ -173,6 +175,15 @@ pub const Feature = enum { |
| 173 | /// Not compatible with `scalarize_mul_safe`. | 175 | /// Not compatible with `scalarize_mul_safe`. |
| 174 | expand_mul_safe, | 176 | expand_mul_safe, |
| 175 | 177 | ||
| 178 | /// Replace `div_ceil` with truncating division followed by a remainder based adjustment for integers, | ||
| 179 | /// or division followed by ceil for floats. | ||
| 180 | /// Not compatible with `scalarize_div_ceil`. | ||
| 181 | expand_div_ceil, | ||
| 182 | /// Replace `div_ceil_optimized` with truncating division followed by a remainder based adjustment for integers, | ||
| 183 | /// or division followed by ceil for floats. | ||
| 184 | /// Not compatible with `scalarize_div_ceil_optimized`. | ||
| 185 | expand_div_ceil_optimized, | ||
| 186 | |||
| 176 | /// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`. | 187 | /// Replace `load` from a packed pointer with a non-packed `load`, `shr`, `truncate`. |
| 177 | /// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the | 188 | /// Currently assumes little endian and a specific integer layout where the lsb of every integer is the lsb of the |
| 178 | /// first byte of memory until bit pointers know their backing type. | 189 | /// first byte of memory until bit pointers know their backing type. |
| ... | @@ -231,6 +242,8 @@ pub const Feature = enum { | ... | @@ -231,6 +242,8 @@ pub const Feature = enum { |
| 231 | .div_trunc_optimized => .scalarize_div_trunc_optimized, | 242 | .div_trunc_optimized => .scalarize_div_trunc_optimized, |
| 232 | .div_floor => .scalarize_div_floor, | 243 | .div_floor => .scalarize_div_floor, |
| 233 | .div_floor_optimized => .scalarize_div_floor_optimized, | 244 | .div_floor_optimized => .scalarize_div_floor_optimized, |
| 245 | .div_ceil => .scalarize_div_ceil, | ||
| 246 | .div_ceil_optimized => .scalarize_div_ceil_optimized, | ||
| 234 | .div_exact => .scalarize_div_exact, | 247 | .div_exact => .scalarize_div_exact, |
| 235 | .div_exact_optimized => .scalarize_div_exact_optimized, | 248 | .div_exact_optimized => .scalarize_div_exact_optimized, |
| 236 | .rem => .scalarize_rem, | 249 | .rem => .scalarize_rem, |
| ... | @@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -382,7 +395,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 382 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) { | 395 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) { |
| 383 | .none => {}, | 396 | .none => {}, |
| 384 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)), | 397 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)), |
| 385 | .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorBlockPayload( | 398 | .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorCeilBlockPayload( |
| 386 | inst, | 399 | inst, |
| 387 | bin_op.lhs, | 400 | bin_op.lhs, |
| 388 | bin_op.rhs, | 401 | bin_op.rhs, |
| ... | @@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -596,6 +609,30 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 596 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); | 609 | continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .ty_op)); |
| 597 | } | 610 | } |
| 598 | }, | 611 | }, |
| 612 | inline .div_ceil, .div_ceil_optimized => |air_tag| { | ||
| 613 | const expand_feature: Feature = switch (air_tag) { | ||
| 614 | .div_ceil => .expand_div_ceil, | ||
| 615 | .div_ceil_optimized => .expand_div_ceil_optimized, | ||
| 616 | else => unreachable, | ||
| 617 | }; | ||
| 618 | |||
| 619 | if (l.features.has(expand_feature)) { | ||
| 620 | assert(!l.features.has(.scalarize(air_tag))); // it doesn't make sense to do both | ||
| 621 | continue :inst l.replaceInst(inst, .block, try l.divCeilBlockPayload(inst, air_tag)); | ||
| 622 | } else { | ||
| 623 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 624 | switch (l.wantScalarizeOrSoftFloat(air_tag, l.typeOf(bin_op.lhs))) { | ||
| 625 | .none => {}, | ||
| 626 | .scalarize => continue :inst l.replaceInst(inst, .block, try l.scalarizeBlockPayload(inst, .bin_op)), | ||
| 627 | .soft_float => continue :inst l.replaceInst(inst, .block, try l.softFloatDivTruncFloorCeilBlockPayload( | ||
| 628 | inst, | ||
| 629 | bin_op.lhs, | ||
| 630 | bin_op.rhs, | ||
| 631 | air_tag, | ||
| 632 | )), | ||
| 633 | } | ||
| 634 | } | ||
| 635 | }, | ||
| 599 | inline .int_from_float_safe, | 636 | inline .int_from_float_safe, |
| 600 | .int_from_float_optimized_safe, | 637 | .int_from_float_optimized_safe, |
| 601 | => |air_tag| { | 638 | => |air_tag| { |
| ... | @@ -2419,6 +2456,159 @@ fn safeArithmeticBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, overflow_ | ... | @@ -2419,6 +2456,159 @@ fn safeArithmeticBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, overflow_ |
| 2419 | } }; | 2456 | } }; |
| 2420 | } | 2457 | } |
| 2421 | 2458 | ||
| 2459 | fn divCeilBlockPayload( | ||
| 2460 | l: *Legalize, | ||
| 2461 | orig_inst: Air.Inst.Index, | ||
| 2462 | air_tag: Air.Inst.Tag, | ||
| 2463 | ) Error!Air.Inst.Data { | ||
| 2464 | const pt = l.pt; | ||
| 2465 | const zcu = pt.zcu; | ||
| 2466 | const gpa = zcu.gpa; | ||
| 2467 | |||
| 2468 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].bin_op; | ||
| 2469 | const operand_ty = l.typeOf(bin_op.lhs); | ||
| 2470 | assert(l.typeOf(bin_op.rhs).toIntern() == operand_ty.toIntern()); | ||
| 2471 | |||
| 2472 | const scalar_ty = operand_ty.scalarType(zcu); | ||
| 2473 | const is_vector = operand_ty.zigTypeTag(zcu) == .vector; | ||
| 2474 | |||
| 2475 | switch (scalar_ty.zigTypeTag(zcu)) { | ||
| 2476 | .float => { | ||
| 2477 | // %result = ceil(lhs / rhs) | ||
| 2478 | |||
| 2479 | var inst_buf: [3]Air.Inst.Index = undefined; | ||
| 2480 | try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len); | ||
| 2481 | |||
| 2482 | var main_block: Block = .init(&inst_buf); | ||
| 2483 | |||
| 2484 | const div_tag: Air.Inst.Tag = switch (air_tag) { | ||
| 2485 | .div_ceil => .div_float, | ||
| 2486 | .div_ceil_optimized => .div_float_optimized, | ||
| 2487 | else => unreachable, | ||
| 2488 | }; | ||
| 2489 | |||
| 2490 | const div_inst = main_block.add(l, .{ | ||
| 2491 | .tag = div_tag, | ||
| 2492 | .data = .{ .bin_op = bin_op }, | ||
| 2493 | }); | ||
| 2494 | |||
| 2495 | const ceil_inst = main_block.add(l, .{ | ||
| 2496 | .tag = .ceil, | ||
| 2497 | .data = .{ .un_op = div_inst.toRef() }, | ||
| 2498 | }); | ||
| 2499 | |||
| 2500 | main_block.addBr(l, orig_inst, ceil_inst.toRef()); | ||
| 2501 | |||
| 2502 | _ = main_block.stealRemainingCapacity(); | ||
| 2503 | return .{ .ty_pl = .{ | ||
| 2504 | .ty = .fromType(operand_ty), | ||
| 2505 | .payload = try l.addBlockBody(main_block.body()), | ||
| 2506 | } }; | ||
| 2507 | }, | ||
| 2508 | |||
| 2509 | .int => { | ||
| 2510 | // Integer div_ceil: | ||
| 2511 | // | ||
| 2512 | // q = div_trunc(lhs, rhs) | ||
| 2513 | // r = rem(lhs, rhs) | ||
| 2514 | // | ||
| 2515 | // unsigned: | ||
| 2516 | // q + int(r != 0) | ||
| 2517 | // | ||
| 2518 | // signed: | ||
| 2519 | // q + int(r != 0 and same_sign(lhs, rhs)) | ||
| 2520 | // | ||
| 2521 | // same_sign is `(lhs ^ rhs) >= 0`. | ||
| 2522 | |||
| 2523 | var inst_buf: [10]Air.Inst.Index = undefined; | ||
| 2524 | try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len); | ||
| 2525 | |||
| 2526 | var main_block: Block = .init(&inst_buf); | ||
| 2527 | |||
| 2528 | const q_inst = main_block.add(l, .{ | ||
| 2529 | .tag = .div_trunc, | ||
| 2530 | .data = .{ .bin_op = bin_op }, | ||
| 2531 | }); | ||
| 2532 | |||
| 2533 | const r_inst = main_block.add(l, .{ | ||
| 2534 | .tag = .rem, | ||
| 2535 | .data = .{ .bin_op = bin_op }, | ||
| 2536 | }); | ||
| 2537 | |||
| 2538 | const zero_ref: Air.Inst.Ref = if (is_vector) zero: { | ||
| 2539 | const zero_scalar = try pt.intValue(scalar_ty, 0); | ||
| 2540 | const zero_vec = try pt.aggregateSplatValue(operand_ty, zero_scalar); | ||
| 2541 | break :zero Air.internedToRef(zero_vec.toIntern()); | ||
| 2542 | } else Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern()); | ||
| 2543 | |||
| 2544 | const r_nonzero_inst = try main_block.addCmp( | ||
| 2545 | l, | ||
| 2546 | .neq, | ||
| 2547 | r_inst.toRef(), | ||
| 2548 | zero_ref, | ||
| 2549 | .{ .vector = is_vector }, | ||
| 2550 | ); | ||
| 2551 | |||
| 2552 | const int_info = scalar_ty.intInfo(zcu); | ||
| 2553 | |||
| 2554 | const need_adjust_inst: Air.Inst.Index = if (int_info.signedness == .unsigned) r_nonzero_inst else inst: { | ||
| 2555 | const sign_xor_inst = main_block.add(l, .{ | ||
| 2556 | .tag = .xor, | ||
| 2557 | .data = .{ .bin_op = .{ | ||
| 2558 | .lhs = bin_op.lhs, | ||
| 2559 | .rhs = bin_op.rhs, | ||
| 2560 | } }, | ||
| 2561 | }); | ||
| 2562 | |||
| 2563 | const signs_same_inst = try main_block.addCmp( | ||
| 2564 | l, | ||
| 2565 | .gte, | ||
| 2566 | sign_xor_inst.toRef(), | ||
| 2567 | zero_ref, | ||
| 2568 | .{ .vector = is_vector }, | ||
| 2569 | ); | ||
| 2570 | |||
| 2571 | break :inst main_block.add(l, .{ | ||
| 2572 | .tag = .bit_and, | ||
| 2573 | .data = .{ .bin_op = .{ | ||
| 2574 | .lhs = r_nonzero_inst.toRef(), | ||
| 2575 | .rhs = signs_same_inst.toRef(), | ||
| 2576 | } }, | ||
| 2577 | }); | ||
| 2578 | }; | ||
| 2579 | |||
| 2580 | const adjust_u1_ty = if (is_vector) | ||
| 2581 | try pt.vectorType(.{ | ||
| 2582 | .len = operand_ty.vectorLen(zcu), | ||
| 2583 | .child = Type.u1.toIntern(), | ||
| 2584 | }) | ||
| 2585 | else | ||
| 2586 | Type.u1; | ||
| 2587 | |||
| 2588 | const adjust_u1_ref = main_block.addBitCast(l, adjust_u1_ty, need_adjust_inst.toRef()); | ||
| 2589 | const adjust_inst = main_block.addTyOp(l, .int_cast, operand_ty, adjust_u1_ref); | ||
| 2590 | |||
| 2591 | const result_inst = main_block.add(l, .{ | ||
| 2592 | .tag = .add, | ||
| 2593 | .data = .{ .bin_op = .{ | ||
| 2594 | .lhs = q_inst.toRef(), | ||
| 2595 | .rhs = adjust_inst.toRef(), | ||
| 2596 | } }, | ||
| 2597 | }); | ||
| 2598 | |||
| 2599 | main_block.addBr(l, orig_inst, result_inst.toRef()); | ||
| 2600 | |||
| 2601 | _ = main_block.stealRemainingCapacity(); | ||
| 2602 | return .{ .ty_pl = .{ | ||
| 2603 | .ty = .fromType(operand_ty), | ||
| 2604 | .payload = try l.addBlockBody(main_block.body()), | ||
| 2605 | } }; | ||
| 2606 | }, | ||
| 2607 | |||
| 2608 | else => unreachable, | ||
| 2609 | } | ||
| 2610 | } | ||
| 2611 | |||
| 2422 | fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { | 2612 | fn packedLoadBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { |
| 2423 | const pt = l.pt; | 2613 | const pt = l.pt; |
| 2424 | const zcu = pt.zcu; | 2614 | const zcu = pt.zcu; |
| ... | @@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload( | ... | @@ -3426,7 +3616,7 @@ fn softFloatNegBlockPayload( |
| 3426 | } }; | 3616 | } }; |
| 3427 | } | 3617 | } |
| 3428 | 3618 | ||
| 3429 | fn softFloatDivTruncFloorBlockPayload( | 3619 | fn softFloatDivTruncFloorCeilBlockPayload( |
| 3430 | l: *Legalize, | 3620 | l: *Legalize, |
| 3431 | orig_inst: Air.Inst.Index, | 3621 | orig_inst: Air.Inst.Index, |
| 3432 | lhs: Air.Inst.Ref, | 3622 | lhs: Air.Inst.Ref, |
| ... | @@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload( | ... | @@ -3441,6 +3631,7 @@ fn softFloatDivTruncFloorBlockPayload( |
| 3441 | const floor_tag: Air.Inst.Tag = switch (air_tag) { | 3631 | const floor_tag: Air.Inst.Tag = switch (air_tag) { |
| 3442 | .div_trunc, .div_trunc_optimized => .trunc_float, | 3632 | .div_trunc, .div_trunc_optimized => .trunc_float, |
| 3443 | .div_floor, .div_floor_optimized => .floor, | 3633 | .div_floor, .div_floor_optimized => .floor, |
| 3634 | .div_ceil, .div_ceil_optimized => .ceil, | ||
| 3444 | else => unreachable, | 3635 | else => unreachable, |
| 3445 | }; | 3636 | }; |
| 3446 | 3637 |
src/Air/Liveness.zig+2| ... | @@ -417,6 +417,8 @@ fn analyzeInst( | ... | @@ -417,6 +417,8 @@ fn analyzeInst( |
| 417 | .div_floor_optimized, | 417 | .div_floor_optimized, |
| 418 | .div_exact, | 418 | .div_exact, |
| 419 | .div_exact_optimized, | 419 | .div_exact_optimized, |
| 420 | .div_ceil, | ||
| 421 | .div_ceil_optimized, | ||
| 420 | .rem, | 422 | .rem, |
| 421 | .rem_optimized, | 423 | .rem_optimized, |
| 422 | .mod, | 424 | .mod, |
src/Air/Liveness/Verify.zig+2| ... | @@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { | ... | @@ -235,6 +235,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 235 | .div_trunc_optimized, | 235 | .div_trunc_optimized, |
| 236 | .div_floor, | 236 | .div_floor, |
| 237 | .div_floor_optimized, | 237 | .div_floor_optimized, |
| 238 | .div_ceil, | ||
| 239 | .div_ceil_optimized, | ||
| 238 | .div_exact, | 240 | .div_exact, |
| 239 | .div_exact_optimized, | 241 | .div_exact_optimized, |
| 240 | .rem, | 242 | .rem, |
src/Air/Verify.zig+2| ... | @@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void { | ... | @@ -253,6 +253,8 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void { |
| 253 | .div_trunc_optimized, | 253 | .div_trunc_optimized, |
| 254 | .div_floor, | 254 | .div_floor, |
| 255 | .div_floor_optimized, | 255 | .div_floor_optimized, |
| 256 | .div_ceil, | ||
| 257 | .div_ceil_optimized, | ||
| 256 | .div_exact, | 258 | .div_exact, |
| 257 | .div_exact_optimized, | 259 | .div_exact_optimized, |
| 258 | .rem, | 260 | .rem, |
src/Air/print.zig+2| ... | @@ -132,6 +132,7 @@ const Writer = struct { | ... | @@ -132,6 +132,7 @@ const Writer = struct { |
| 132 | .div_float, | 132 | .div_float, |
| 133 | .div_trunc, | 133 | .div_trunc, |
| 134 | .div_floor, | 134 | .div_floor, |
| 135 | .div_ceil, | ||
| 135 | .div_exact, | 136 | .div_exact, |
| 136 | .rem, | 137 | .rem, |
| 137 | .mod, | 138 | .mod, |
| ... | @@ -160,6 +161,7 @@ const Writer = struct { | ... | @@ -160,6 +161,7 @@ const Writer = struct { |
| 160 | .div_float_optimized, | 161 | .div_float_optimized, |
| 161 | .div_trunc_optimized, | 162 | .div_trunc_optimized, |
| 162 | .div_floor_optimized, | 163 | .div_floor_optimized, |
| 164 | .div_ceil_optimized, | ||
| 163 | .div_exact_optimized, | 165 | .div_exact_optimized, |
| 164 | .rem_optimized, | 166 | .rem_optimized, |
| 165 | .mod_optimized, | 167 | .mod_optimized, |
src/Sema.zig+68-2| ... | @@ -1335,6 +1335,7 @@ fn analyzeBodyInner( | ... | @@ -1335,6 +1335,7 @@ fn analyzeBodyInner( |
| 1335 | .div => try sema.zirDiv(block, inst), | 1335 | .div => try sema.zirDiv(block, inst), |
| 1336 | .div_exact => try sema.zirDivExact(block, inst), | 1336 | .div_exact => try sema.zirDivExact(block, inst), |
| 1337 | .div_floor => try sema.zirDivFloor(block, inst), | 1337 | .div_floor => try sema.zirDivFloor(block, inst), |
| 1338 | .div_ceil => try sema.zirDivCeil(block, inst), | ||
| 1338 | .div_trunc => try sema.zirDivTrunc(block, inst), | 1339 | .div_trunc => try sema.zirDivTrunc(block, inst), |
| 1339 | 1340 | ||
| 1340 | .mod_rem => try sema.zirModRem(block, inst), | 1341 | .mod_rem => try sema.zirModRem(block, inst), |
| ... | @@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins | ... | @@ -13850,7 +13851,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 13850 | return sema.fail( | 13851 | return sema.fail( |
| 13851 | block, | 13852 | block, |
| 13852 | src, | 13853 | src, |
| 13853 | "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, or @divExact", | 13854 | "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact", |
| 13854 | .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) }, | 13855 | .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) }, |
| 13855 | ); | 13856 | ); |
| 13856 | } | 13857 | } |
| ... | @@ -14023,6 +14024,71 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -14023,6 +14024,71 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 14023 | return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); | 14024 | return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); |
| 14024 | } | 14025 | } |
| 14025 | 14026 | ||
| 14027 | fn zirDivCeil(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 14028 | const pt = sema.pt; | ||
| 14029 | const zcu = pt.zcu; | ||
| 14030 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | ||
| 14031 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); | ||
| 14032 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); | ||
| 14033 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); | ||
| 14034 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | ||
| 14035 | const lhs = sema.resolveInst(extra.lhs); | ||
| 14036 | const rhs = sema.resolveInst(extra.rhs); | ||
| 14037 | const lhs_ty = sema.typeOf(lhs); | ||
| 14038 | const rhs_ty = sema.typeOf(rhs); | ||
| 14039 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); | ||
| 14040 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); | ||
| 14041 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); | ||
| 14042 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); | ||
| 14043 | |||
| 14044 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ | ||
| 14045 | .override = &.{ lhs_src, rhs_src }, | ||
| 14046 | }); | ||
| 14047 | |||
| 14048 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); | ||
| 14049 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); | ||
| 14050 | |||
| 14051 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); | ||
| 14052 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); | ||
| 14053 | |||
| 14054 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; | ||
| 14055 | |||
| 14056 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_ceil); | ||
| 14057 | |||
| 14058 | const maybe_lhs_val = sema.resolveValue(casted_lhs); | ||
| 14059 | const maybe_rhs_val = sema.resolveValue(casted_rhs); | ||
| 14060 | |||
| 14061 | const allow_div_zero = !is_int and | ||
| 14062 | resolved_type.toIntern() != .comptime_float_type and | ||
| 14063 | block.float_mode == .strict; | ||
| 14064 | |||
| 14065 | if (maybe_lhs_val) |lhs_val| { | ||
| 14066 | if (maybe_rhs_val) |rhs_val| { | ||
| 14067 | const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_ceil); | ||
| 14068 | return Air.internedToRef(result.toIntern()); | ||
| 14069 | } | ||
| 14070 | if (allow_div_zero) { | ||
| 14071 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); | ||
| 14072 | } else { | ||
| 14073 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); | ||
| 14074 | } | ||
| 14075 | } else if (maybe_rhs_val) |rhs_val| { | ||
| 14076 | if (allow_div_zero) { | ||
| 14077 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); | ||
| 14078 | } else { | ||
| 14079 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); | ||
| 14080 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); | ||
| 14081 | } | ||
| 14082 | } | ||
| 14083 | |||
| 14084 | if (block.wantSafety()) { | ||
| 14085 | try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); | ||
| 14086 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); | ||
| 14087 | } | ||
| 14088 | |||
| 14089 | return block.addBinOp(airTag(block, is_int, .div_ceil, .div_ceil_optimized), casted_lhs, casted_rhs); | ||
| 14090 | } | ||
| 14091 | |||
| 14026 | fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 14092 | fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14027 | const pt = sema.pt; | 14093 | const pt = sema.pt; |
| 14028 | const zcu = pt.zcu; | 14094 | const zcu = pt.zcu; |
| ... | @@ -18051,7 +18117,7 @@ fn analyzeRet( | ... | @@ -18051,7 +18117,7 @@ fn analyzeRet( |
| 18051 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { | 18117 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { |
| 18052 | // extend this swich as additional operators are implemented | 18118 | // extend this swich as additional operators are implemented |
| 18053 | return switch (tag) { | 18119 | return switch (tag) { |
| 18054 | .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true, | 18120 | .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .div_ceil, .mod, .rem, .mod_rem => true, |
| 18055 | else => false, | 18121 | else => false, |
| 18056 | }; | 18122 | }; |
| 18057 | } | 18123 | } |
src/Sema/arith.zig+68-2| ... | @@ -768,7 +768,7 @@ fn mulSatScalar( | ... | @@ -768,7 +768,7 @@ fn mulSatScalar( |
| 768 | } | 768 | } |
| 769 | } | 769 | } |
| 770 | 770 | ||
| 771 | pub const DivOp = enum { div, div_trunc, div_floor, div_exact }; | 771 | pub const DivOp = enum { div, div_trunc, div_floor, div_ceil, div_exact }; |
| 772 | 772 | ||
| 773 | /// Applies the `/` operator to comptime-known values. | 773 | /// Applies the `/` operator to comptime-known values. |
| 774 | /// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`. | 774 | /// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`. |
| ... | @@ -843,6 +843,11 @@ fn divScalar( | ... | @@ -843,6 +843,11 @@ fn divScalar( |
| 843 | if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx); | 843 | if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx); |
| 844 | return res.val; | 844 | return res.val; |
| 845 | }, | 845 | }, |
| 846 | .div_ceil => { | ||
| 847 | const res = try intDivCeil(sema, lhs_val, rhs_val, ty); | ||
| 848 | if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx); | ||
| 849 | return res.val; | ||
| 850 | }, | ||
| 846 | .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) { | 851 | .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) { |
| 847 | .remainder => return sema.fail(block, src, "exact division produced remainder", .{}), | 852 | .remainder => return sema.fail(block, src, "exact division produced remainder", .{}), |
| 848 | .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx), | 853 | .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx), |
| ... | @@ -851,7 +856,7 @@ fn divScalar( | ... | @@ -851,7 +856,7 @@ fn divScalar( |
| 851 | } | 856 | } |
| 852 | } else { | 857 | } else { |
| 853 | const allow_div_zero = switch (op) { | 858 | const allow_div_zero = switch (op) { |
| 854 | .div, .div_trunc, .div_floor => ty.toIntern() != .comptime_float_type and block.float_mode == .strict, | 859 | .div, .div_trunc, .div_floor, .div_ceil => ty.toIntern() != .comptime_float_type and block.float_mode == .strict, |
| 855 | .div_exact => false, | 860 | .div_exact => false, |
| 856 | }; | 861 | }; |
| 857 | if (!allow_div_zero) { | 862 | if (!allow_div_zero) { |
| ... | @@ -871,6 +876,7 @@ fn divScalar( | ... | @@ -871,6 +876,7 @@ fn divScalar( |
| 871 | .div => return floatDiv(sema, lhs_val, rhs_val, ty), | 876 | .div => return floatDiv(sema, lhs_val, rhs_val, ty), |
| 872 | .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty), | 877 | .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty), |
| 873 | .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty), | 878 | .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty), |
| 879 | .div_ceil => return floatDivCeil(sema, lhs_val, rhs_val, ty), | ||
| 874 | .div_exact => { | 880 | .div_exact => { |
| 875 | if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) { | 881 | if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) { |
| 876 | return sema.fail(block, src, "exact division produced remainder", .{}); | 882 | return sema.fail(block, src, "exact division produced remainder", .{}); |
| ... | @@ -1755,6 +1761,49 @@ fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { | ... | @@ -1755,6 +1761,49 @@ fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { |
| 1755 | } | 1761 | } |
| 1756 | return pt.intValue_big(ty, result_q.toConst()); | 1762 | return pt.intValue_big(ty, result_q.toConst()); |
| 1757 | } | 1763 | } |
| 1764 | fn intDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } { | ||
| 1765 | const result = intDivCeilInner(sema, lhs, rhs, ty) catch |err| switch (err) { | ||
| 1766 | error.Overflow => { | ||
| 1767 | const result = intDivCeilInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) { | ||
| 1768 | error.Overflow => unreachable, | ||
| 1769 | else => |e| return e, | ||
| 1770 | }; | ||
| 1771 | return .{ .overflow = true, .val = result }; | ||
| 1772 | }, | ||
| 1773 | else => |e| return e, | ||
| 1774 | }; | ||
| 1775 | return .{ .overflow = false, .val = result }; | ||
| 1776 | } | ||
| 1777 | fn intDivCeilInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { | ||
| 1778 | const pt = sema.pt; | ||
| 1779 | const zcu = pt.zcu; | ||
| 1780 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 1781 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 1782 | const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); | ||
| 1783 | const rhs_bigint = rhs.toBigInt(&rhs_space, zcu); | ||
| 1784 | const limbs_q = try sema.arena.alloc( | ||
| 1785 | std.math.big.Limb, | ||
| 1786 | lhs_bigint.limbs.len, | ||
| 1787 | ); | ||
| 1788 | const limbs_r = try sema.arena.alloc( | ||
| 1789 | std.math.big.Limb, | ||
| 1790 | rhs_bigint.limbs.len, | ||
| 1791 | ); | ||
| 1792 | const limbs_buf = try sema.arena.alloc( | ||
| 1793 | std.math.big.Limb, | ||
| 1794 | std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | ||
| 1795 | ); | ||
| 1796 | var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined }; | ||
| 1797 | var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined }; | ||
| 1798 | result_q.divCeil(&result_r, lhs_bigint, rhs_bigint, limbs_buf); | ||
| 1799 | if (ty.toIntern() != .comptime_int_type) { | ||
| 1800 | const info = ty.intInfo(zcu); | ||
| 1801 | if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) { | ||
| 1802 | return error.Overflow; | ||
| 1803 | } | ||
| 1804 | } | ||
| 1805 | return pt.intValue_big(ty, result_q.toConst()); | ||
| 1806 | } | ||
| 1758 | fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { | 1807 | fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { |
| 1759 | const pt = sema.pt; | 1808 | const pt = sema.pt; |
| 1760 | const zcu = pt.zcu; | 1809 | const zcu = pt.zcu; |
| ... | @@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { | ... | @@ -2140,6 +2189,23 @@ fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { |
| 2140 | .storage = storage, | 2189 | .storage = storage, |
| 2141 | } })); | 2190 | } })); |
| 2142 | } | 2191 | } |
| 2192 | fn floatDivCeil(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value { | ||
| 2193 | const pt = sema.pt; | ||
| 2194 | const zcu = pt.zcu; | ||
| 2195 | const target = zcu.getTarget(); | ||
| 2196 | const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) { // TODO | ||
| 2197 | 16 => .{ .f16 = @ceil(lhs.toFloat(f16, zcu) / rhs.toFloat(f16, zcu)) }, | ||
| 2198 | 32 => .{ .f32 = @ceil(lhs.toFloat(f32, zcu) / rhs.toFloat(f32, zcu)) }, | ||
| 2199 | 64 => .{ .f64 = @ceil(lhs.toFloat(f64, zcu) / rhs.toFloat(f64, zcu)) }, | ||
| 2200 | 80 => .{ .f80 = @ceil(lhs.toFloat(f80, zcu) / rhs.toFloat(f80, zcu)) }, | ||
| 2201 | 128 => .{ .f128 = @ceil(lhs.toFloat(f128, zcu) / rhs.toFloat(f128, zcu)) }, | ||
| 2202 | else => unreachable, | ||
| 2203 | }; | ||
| 2204 | return .fromInterned(try pt.intern(.{ .float = .{ | ||
| 2205 | .ty = ty.toIntern(), | ||
| 2206 | .storage = storage, | ||
| 2207 | } })); | ||
| 2208 | } | ||
| 2143 | fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool { | 2209 | fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool { |
| 2144 | const zcu = sema.pt.zcu; | 2210 | const zcu = sema.pt.zcu; |
| 2145 | const target = zcu.getTarget(); | 2211 | const target = zcu.getTarget(); |
src/codegen/aarch64/Select.zig+2| ... | @@ -175,6 +175,8 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { | ... | @@ -175,6 +175,8 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { |
| 175 | .div_trunc_optimized, | 175 | .div_trunc_optimized, |
| 176 | .div_floor, | 176 | .div_floor, |
| 177 | .div_floor_optimized, | 177 | .div_floor_optimized, |
| 178 | .div_ceil, | ||
| 179 | .div_ceil_optimized, | ||
| 178 | .div_exact, | 180 | .div_exact, |
| 179 | .div_exact_optimized, | 181 | .div_exact_optimized, |
| 180 | .rem, | 182 | .rem, |
src/codegen/c.zig+2| ... | @@ -2675,6 +2675,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { | ... | @@ -2675,6 +2675,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { |
| 2675 | try airBinBuiltinCall(f, inst, "fmod", .none); | 2675 | try airBinBuiltinCall(f, inst, "fmod", .none); |
| 2676 | }, | 2676 | }, |
| 2677 | .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none), | 2677 | .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none), |
| 2678 | .div_ceil => try airBinBuiltinCall(f, inst, "div_ceil", .none), | ||
| 2678 | .mod => try airBinBuiltinCall(f, inst, "mod", .none), | 2679 | .mod => try airBinBuiltinCall(f, inst, "mod", .none), |
| 2679 | .abs => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none), | 2680 | .abs => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none), |
| 2680 | 2681 | ||
| ... | @@ -2856,6 +2857,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { | ... | @@ -2856,6 +2857,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { |
| 2856 | .div_float_optimized, | 2857 | .div_float_optimized, |
| 2857 | .div_trunc_optimized, | 2858 | .div_trunc_optimized, |
| 2858 | .div_floor_optimized, | 2859 | .div_floor_optimized, |
| 2860 | .div_ceil_optimized, | ||
| 2859 | .div_exact_optimized, | 2861 | .div_exact_optimized, |
| 2860 | .rem_optimized, | 2862 | .rem_optimized, |
| 2861 | .mod_optimized, | 2863 | .mod_optimized, |
src/codegen/llvm/FuncGen.zig+74| ... | @@ -383,6 +383,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov | ... | @@ -383,6 +383,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov |
| 383 | .div_float => try self.airDivFloat(inst, .normal), | 383 | .div_float => try self.airDivFloat(inst, .normal), |
| 384 | .div_trunc => try self.airDivTrunc(inst, .normal), | 384 | .div_trunc => try self.airDivTrunc(inst, .normal), |
| 385 | .div_floor => try self.airDivFloor(inst, .normal), | 385 | .div_floor => try self.airDivFloor(inst, .normal), |
| 386 | .div_ceil => try self.airDivCeil(inst, .normal), | ||
| 386 | .div_exact => try self.airDivExact(inst, .normal), | 387 | .div_exact => try self.airDivExact(inst, .normal), |
| 387 | .rem => try self.airRem(inst, .normal), | 388 | .rem => try self.airRem(inst, .normal), |
| 388 | .mod => try self.airMod(inst, .normal), | 389 | .mod => try self.airMod(inst, .normal), |
| ... | @@ -400,6 +401,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov | ... | @@ -400,6 +401,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov |
| 400 | .div_float_optimized => try self.airDivFloat(inst, .fast), | 401 | .div_float_optimized => try self.airDivFloat(inst, .fast), |
| 401 | .div_trunc_optimized => try self.airDivTrunc(inst, .fast), | 402 | .div_trunc_optimized => try self.airDivTrunc(inst, .fast), |
| 402 | .div_floor_optimized => try self.airDivFloor(inst, .fast), | 403 | .div_floor_optimized => try self.airDivFloor(inst, .fast), |
| 404 | .div_ceil_optimized => try self.airDivCeil(inst, .fast), | ||
| 403 | .div_exact_optimized => try self.airDivExact(inst, .fast), | 405 | .div_exact_optimized => try self.airDivExact(inst, .fast), |
| 404 | .rem_optimized => try self.airRem(inst, .fast), | 406 | .rem_optimized => try self.airRem(inst, .fast), |
| 405 | .mod_optimized => try self.airMod(inst, .fast), | 407 | .mod_optimized => try self.airMod(inst, .fast), |
| ... | @@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) | ... | @@ -3578,6 +3580,78 @@ fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) |
| 3578 | return self.wip.bin(.udiv, lhs, rhs, ""); | 3580 | return self.wip.bin(.udiv, lhs, rhs, ""); |
| 3579 | } | 3581 | } |
| 3580 | 3582 | ||
| 3583 | fn airDivCeil(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { | ||
| 3584 | const o = self.object; | ||
| 3585 | const zcu = o.zcu; | ||
| 3586 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 3587 | const lhs = try self.resolveInst(bin_op.lhs); | ||
| 3588 | const rhs = try self.resolveInst(bin_op.rhs); | ||
| 3589 | const inst_ty = self.typeOfIndex(inst); | ||
| 3590 | const scalar_ty = inst_ty.scalarType(zcu); | ||
| 3591 | |||
| 3592 | if (scalar_ty.isRuntimeFloat()) { | ||
| 3593 | const result = try self.buildFloatOp(.div, fast, inst_ty, 2, .{ lhs, rhs }); | ||
| 3594 | return self.buildFloatOp(.ceil, fast, inst_ty, 1, .{result}); | ||
| 3595 | } | ||
| 3596 | if (scalar_ty.isSignedInt(zcu)) { | ||
| 3597 | const scalar_llvm_ty = try o.lowerType(scalar_ty, .by_value); | ||
| 3598 | const inst_llvm_ty = try o.lowerType(inst_ty, .by_value); | ||
| 3599 | |||
| 3600 | const ExpectedContents = [std.math.big.int.calcTwosCompLimbCount(256)]std.math.big.Limb; | ||
| 3601 | var bfa_buf: ExpectedContents = undefined; | ||
| 3602 | var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), self.gpa); | ||
| 3603 | const allocator = bfa.allocator(); | ||
| 3604 | |||
| 3605 | const scalar_bits = scalar_ty.intInfo(zcu).bits; | ||
| 3606 | var smin_big_int: std.math.big.int.Mutable = .{ | ||
| 3607 | .limbs = try allocator.alloc( | ||
| 3608 | std.math.big.Limb, | ||
| 3609 | std.math.big.int.calcTwosCompLimbCount(scalar_bits), | ||
| 3610 | ), | ||
| 3611 | .len = undefined, | ||
| 3612 | .positive = undefined, | ||
| 3613 | }; | ||
| 3614 | defer allocator.free(smin_big_int.limbs); | ||
| 3615 | smin_big_int.setTwosCompIntLimit(.min, .signed, scalar_bits); | ||
| 3616 | const smin = try o.builder.splatValue(inst_llvm_ty, try o.builder.bigIntConst( | ||
| 3617 | scalar_llvm_ty, | ||
| 3618 | smin_big_int.toConst(), | ||
| 3619 | )); | ||
| 3620 | |||
| 3621 | const zero = try o.builder.splatValue( | ||
| 3622 | inst_llvm_ty, | ||
| 3623 | try o.builder.intConst(scalar_llvm_ty, 0), | ||
| 3624 | ); | ||
| 3625 | |||
| 3626 | const div = try self.wip.bin(.sdiv, lhs, rhs, "divCeil.div"); | ||
| 3627 | const rem = try self.wip.bin(.srem, lhs, rhs, "divCeil.rem"); | ||
| 3628 | |||
| 3629 | const rhs_sign = try self.wip.bin(.@"and", rhs, smin, "divCeil.rhs_sign"); | ||
| 3630 | const rem_xor_rhs_sign = try self.wip.bin(.xor, rem, rhs_sign, "divCeil.rem_xor_rhs_sign"); | ||
| 3631 | |||
| 3632 | const need_correction = try self.wip.icmp(.sgt, rem_xor_rhs_sign, zero, "divCeil.need_correction"); | ||
| 3633 | |||
| 3634 | const correction = try self.wip.cast(.zext, need_correction, inst_llvm_ty, "divCeil.correction"); | ||
| 3635 | return self.wip.bin(.@"add nsw", div, correction, "divCeil"); | ||
| 3636 | } else { | ||
| 3637 | const scalar_llvm_ty = try o.lowerType(scalar_ty, .by_value); | ||
| 3638 | const inst_llvm_ty = try o.lowerType(inst_ty, .by_value); | ||
| 3639 | |||
| 3640 | const zero = try o.builder.splatValue( | ||
| 3641 | inst_llvm_ty, | ||
| 3642 | try o.builder.intConst(scalar_llvm_ty, 0), | ||
| 3643 | ); | ||
| 3644 | |||
| 3645 | const div = try self.wip.bin(.udiv, lhs, rhs, "divCeil.div"); | ||
| 3646 | const rem = try self.wip.bin(.urem, lhs, rhs, "divCeil.rem"); | ||
| 3647 | |||
| 3648 | const rem_nonzero = try self.wip.icmp(.ne, rem, zero, "divCeil.rem_nonzero"); | ||
| 3649 | const correction = try self.wip.cast(.zext, rem_nonzero, inst_llvm_ty, "divCeil.correction"); | ||
| 3650 | |||
| 3651 | return self.wip.bin(.@"add nuw", div, correction, "divCeil"); | ||
| 3652 | } | ||
| 3653 | } | ||
| 3654 | |||
| 3581 | fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { | 3655 | fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { |
| 3582 | const zcu = self.object.zcu; | 3656 | const zcu = self.object.zcu; |
| 3583 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3657 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
src/codegen/riscv64/CodeGen.zig+2| ... | @@ -1422,6 +1422,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -1422,6 +1422,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1422 | .mod, | 1422 | .mod, |
| 1423 | .div_float, | 1423 | .div_float, |
| 1424 | .div_floor, | 1424 | .div_floor, |
| 1425 | .div_ceil, | ||
| 1425 | => return func.fail("TODO: {s}", .{@tagName(tag)}), | 1426 | => return func.fail("TODO: {s}", .{@tagName(tag)}), |
| 1426 | 1427 | ||
| 1427 | .sqrt, | 1428 | .sqrt, |
| ... | @@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -1621,6 +1622,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1621 | .div_trunc_optimized, | 1622 | .div_trunc_optimized, |
| 1622 | .div_floor_optimized, | 1623 | .div_floor_optimized, |
| 1623 | .div_exact_optimized, | 1624 | .div_exact_optimized, |
| 1625 | .div_ceil_optimized, | ||
| 1624 | .rem_optimized, | 1626 | .rem_optimized, |
| 1625 | .mod_optimized, | 1627 | .mod_optimized, |
| 1626 | .neg_optimized, | 1628 | .neg_optimized, |
src/codegen/sparc64/CodeGen.zig+2-1| ... | @@ -523,7 +523,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -523,7 +523,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 523 | .mul_with_overflow => try self.airMulWithOverflow(inst), | 523 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 524 | .shl_with_overflow => try self.airShlWithOverflow(inst), | 524 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 525 | 525 | ||
| 526 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | 526 | .div_float, .div_trunc, .div_floor, .div_ceil, .div_exact => try self.airDiv(inst), |
| 527 | 527 | ||
| 528 | .cmp_lt => try self.airCmp(inst, .lt), | 528 | .cmp_lt => try self.airCmp(inst, .lt), |
| 529 | .cmp_lte => try self.airCmp(inst, .lte), | 529 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | @@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -678,6 +678,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 678 | .div_float_optimized, | 678 | .div_float_optimized, |
| 679 | .div_trunc_optimized, | 679 | .div_trunc_optimized, |
| 680 | .div_floor_optimized, | 680 | .div_floor_optimized, |
| 681 | .div_ceil_optimized, | ||
| 681 | .div_exact_optimized, | 682 | .div_exact_optimized, |
| 682 | .rem_optimized, | 683 | .rem_optimized, |
| 683 | .mod_optimized, | 684 | .mod_optimized, |
src/codegen/wasm/CodeGen.zig+103| ... | @@ -62,6 +62,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | ... | @@ -62,6 +62,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 62 | .scalarize_div_trunc_optimized, | 62 | .scalarize_div_trunc_optimized, |
| 63 | .scalarize_div_floor, | 63 | .scalarize_div_floor, |
| 64 | .scalarize_div_floor_optimized, | 64 | .scalarize_div_floor_optimized, |
| 65 | .scalarize_div_ceil, | ||
| 66 | .scalarize_div_ceil_optimized, | ||
| 65 | .scalarize_div_exact, | 67 | .scalarize_div_exact, |
| 66 | .scalarize_div_exact_optimized, | 68 | .scalarize_div_exact_optimized, |
| 67 | .scalarize_rem, | 69 | .scalarize_rem, |
| ... | @@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1340,6 +1342,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1340 | .div_exact, | 1342 | .div_exact, |
| 1341 | .div_trunc, | 1343 | .div_trunc, |
| 1342 | .div_floor, | 1344 | .div_floor, |
| 1345 | .div_ceil, | ||
| 1343 | => |tag| { | 1346 | => |tag| { |
| 1344 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 1347 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 1345 | const lhs = try cg.resolveInst(bin_op.lhs); | 1348 | const lhs = try cg.resolveInst(bin_op.lhs); |
| ... | @@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1366 | .div_exact => try cg.floatDiv(float_ty, lhs, rhs), | 1369 | .div_exact => try cg.floatDiv(float_ty, lhs, rhs), |
| 1367 | .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs), | 1370 | .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs), |
| 1368 | .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs), | 1371 | .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs), |
| 1372 | .div_ceil => try cg.floatDivCeil(float_ty, lhs, rhs), | ||
| 1369 | else => unreachable, | 1373 | else => unreachable, |
| 1370 | }; | 1374 | }; |
| 1371 | 1375 | ||
| ... | @@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1384,6 +1388,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1384 | .div_exact => try cg.intDiv(int_ty, lhs, rhs), | 1388 | .div_exact => try cg.intDiv(int_ty, lhs, rhs), |
| 1385 | .div_trunc => try cg.intDiv(int_ty, lhs, rhs), | 1389 | .div_trunc => try cg.intDiv(int_ty, lhs, rhs), |
| 1386 | .div_floor => try cg.intDivFloor(int_ty, lhs, rhs), | 1390 | .div_floor => try cg.intDivFloor(int_ty, lhs, rhs), |
| 1391 | .div_ceil => try cg.intDivCeil(int_ty, lhs, rhs), | ||
| 1387 | else => unreachable, | 1392 | else => unreachable, |
| 1388 | }; | 1393 | }; |
| 1389 | 1394 | ||
| ... | @@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1881,6 +1886,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1881 | .div_float_optimized, | 1886 | .div_float_optimized, |
| 1882 | .div_trunc_optimized, | 1887 | .div_trunc_optimized, |
| 1883 | .div_floor_optimized, | 1888 | .div_floor_optimized, |
| 1889 | .div_ceil_optimized, | ||
| 1884 | .div_exact_optimized, | 1890 | .div_exact_optimized, |
| 1885 | .rem_optimized, | 1891 | .rem_optimized, |
| 1886 | .mod_optimized, | 1892 | .mod_optimized, |
| ... | @@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W | ... | @@ -2799,6 +2805,97 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W |
| 2799 | } | 2805 | } |
| 2800 | } | 2806 | } |
| 2801 | 2807 | ||
| 2808 | fn intDivCeil(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { | ||
| 2809 | switch (ty.bits) { | ||
| 2810 | 0 => unreachable, | ||
| 2811 | 1...32 => { | ||
| 2812 | var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.i32); | ||
| 2813 | defer q.free(cg); | ||
| 2814 | |||
| 2815 | const zero: WValue = .{ .imm32 = 0 }; | ||
| 2816 | |||
| 2817 | const r = try cg.intRem(ty, lhs, rhs); | ||
| 2818 | var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.i32); | ||
| 2819 | defer r_nonzero.free(cg); | ||
| 2820 | |||
| 2821 | if (!ty.is_signed) { | ||
| 2822 | try cg.emitWValue(q); | ||
| 2823 | try cg.emitWValue(r_nonzero); | ||
| 2824 | try cg.addTag(.i32_add); | ||
| 2825 | return .stack; | ||
| 2826 | } | ||
| 2827 | |||
| 2828 | const sign_xor = try cg.intXor(ty, lhs, rhs); | ||
| 2829 | var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.i32); | ||
| 2830 | defer same_sign.free(cg); | ||
| 2831 | |||
| 2832 | try cg.emitWValue(q); | ||
| 2833 | const need_adjust = try cg.intAnd(.u32, r_nonzero, same_sign); | ||
| 2834 | try cg.emitWValue(need_adjust); | ||
| 2835 | try cg.addTag(.i32_add); | ||
| 2836 | return .stack; | ||
| 2837 | }, | ||
| 2838 | 33...64 => { | ||
| 2839 | var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.i64); | ||
| 2840 | defer q.free(cg); | ||
| 2841 | |||
| 2842 | const zero: WValue = .{ .imm64 = 0 }; | ||
| 2843 | |||
| 2844 | const r = try cg.intRem(ty, lhs, rhs); | ||
| 2845 | var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.i32); | ||
| 2846 | defer r_nonzero.free(cg); | ||
| 2847 | |||
| 2848 | if (!ty.is_signed) { | ||
| 2849 | try cg.emitWValue(q); | ||
| 2850 | try cg.emitWValue(r_nonzero); | ||
| 2851 | try cg.addTag(.i64_extend_i32_u); | ||
| 2852 | try cg.addTag(.i64_add); | ||
| 2853 | return .stack; | ||
| 2854 | } | ||
| 2855 | |||
| 2856 | const sign_xor = try cg.intXor(ty, lhs, rhs); | ||
| 2857 | var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.i32); | ||
| 2858 | defer same_sign.free(cg); | ||
| 2859 | |||
| 2860 | try cg.emitWValue(q); | ||
| 2861 | const need_adjust = try cg.intAnd(.u32, r_nonzero, same_sign); | ||
| 2862 | try cg.emitWValue(need_adjust); | ||
| 2863 | try cg.addTag(.i64_extend_i32_u); | ||
| 2864 | try cg.addTag(.i64_add); | ||
| 2865 | return .stack; | ||
| 2866 | }, | ||
| 2867 | else => { | ||
| 2868 | var q = try (try cg.intDiv(ty, lhs, rhs)).toLocal(cg, Type.usize); | ||
| 2869 | defer q.free(cg); | ||
| 2870 | |||
| 2871 | const zero = try cg.intZeroValue(ty); | ||
| 2872 | |||
| 2873 | const r = try cg.intRem(ty, lhs, rhs); | ||
| 2874 | var r_nonzero = try (try cg.intCmp(ty, .neq, r, zero)).toLocal(cg, Type.u32); | ||
| 2875 | defer r_nonzero.free(cg); | ||
| 2876 | |||
| 2877 | if (!ty.is_signed) { | ||
| 2878 | var adjust_bigint = try (try cg.intCast(ty, .u32, r_nonzero)).toLocal(cg, Type.usize); | ||
| 2879 | defer adjust_bigint.free(cg); | ||
| 2880 | |||
| 2881 | return try cg.intAdd(ty, q, adjust_bigint); | ||
| 2882 | } | ||
| 2883 | |||
| 2884 | const sign_xor = try cg.intXor(ty, lhs, rhs); | ||
| 2885 | var same_sign = try (try cg.intCmp(ty, .gte, sign_xor, zero)).toLocal(cg, Type.u32); | ||
| 2886 | defer same_sign.free(cg); | ||
| 2887 | |||
| 2888 | var adjust = try (try cg.intAnd(.u32, r_nonzero, same_sign)).toLocal(cg, Type.u32); | ||
| 2889 | defer adjust.free(cg); | ||
| 2890 | |||
| 2891 | var adjust_bigint = try (try cg.intCast(ty, .u32, adjust)).toLocal(cg, Type.usize); | ||
| 2892 | defer adjust_bigint.free(cg); | ||
| 2893 | |||
| 2894 | return try cg.intAdd(ty, q, adjust_bigint); | ||
| 2895 | }, | ||
| 2896 | } | ||
| 2897 | } | ||
| 2898 | |||
| 2802 | fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { | 2899 | fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 2803 | switch (ty.bits) { | 2900 | switch (ty.bits) { |
| 2804 | 0 => unreachable, | 2901 | 0 => unreachable, |
| ... | @@ -4265,6 +4362,12 @@ fn floatDivFloor(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerErr | ... | @@ -4265,6 +4362,12 @@ fn floatDivFloor(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerErr |
| 4265 | return cg.floatFloor(ty, div_result); | 4362 | return cg.floatFloor(ty, div_result); |
| 4266 | } | 4363 | } |
| 4267 | 4364 | ||
| 4365 | // div_ceil(a, b) = ceil(a / b) | ||
| 4366 | fn floatDivCeil(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue { | ||
| 4367 | const div_result = try cg.floatDiv(ty, lhs, rhs); | ||
| 4368 | return cg.floatCeil(ty, div_result); | ||
| 4369 | } | ||
| 4370 | |||
| 4268 | // mod(a, b) = fmod(fmod(a, b) + b, b) | 4371 | // mod(a, b) = fmod(fmod(a, b) + b, b) |
| 4269 | fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue { | 4372 | fn floatMod(cg: *CodeGen, ty: FloatType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 4270 | const r = try cg.floatRem(ty, lhs, rhs); | 4373 | const r = try cg.floatRem(ty, lhs, rhs); |
src/codegen/x86_64/CodeGen.zig+5| ... | @@ -70,6 +70,9 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | ... | @@ -70,6 +70,9 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 70 | .expand_sub_safe, | 70 | .expand_sub_safe, |
| 71 | .expand_mul_safe, | 71 | .expand_mul_safe, |
| 72 | 72 | ||
| 73 | .expand_div_ceil, | ||
| 74 | .expand_div_ceil_optimized, | ||
| 75 | |||
| 73 | .expand_packed_load, | 76 | .expand_packed_load, |
| 74 | .expand_packed_store, | 77 | .expand_packed_store, |
| 75 | .expand_packed_agg_field_val, | 78 | .expand_packed_agg_field_val, |
| ... | @@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -173873,6 +173876,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 173873 | for (ops) |op| try op.die(cg); | 173876 | for (ops) |op| try op.die(cg); |
| 173874 | }, | 173877 | }, |
| 173875 | 173878 | ||
| 173879 | .div_ceil, .div_ceil_optimized => unreachable, | ||
| 173880 | |||
| 173876 | // No soft-float `Legalize` features are enabled, so this instruction never appears. | 173881 | // No soft-float `Legalize` features are enabled, so this instruction never appears. |
| 173877 | .legalize_compiler_rt_call => unreachable, | 173882 | .legalize_compiler_rt_call => unreachable, |
| 173878 | 173883 |
src/print_zir.zig+1| ... | @@ -392,6 +392,7 @@ const Writer = struct { | ... | @@ -392,6 +392,7 @@ const Writer = struct { |
| 392 | .truncate, | 392 | .truncate, |
| 393 | .div_exact, | 393 | .div_exact, |
| 394 | .div_floor, | 394 | .div_floor, |
| 395 | .div_ceil, | ||
| 395 | .div_trunc, | 396 | .div_trunc, |
| 396 | .mod, | 397 | .mod, |
| 397 | .rem, | 398 | .rem, |
test/behavior/int128.zig+1| ... | @@ -59,6 +59,7 @@ test "int128" { | ... | @@ -59,6 +59,7 @@ test "int128" { |
| 59 | const a: i128 = -170141183460469231731687303715884105728; | 59 | const a: i128 = -170141183460469231731687303715884105728; |
| 60 | const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000; | 60 | const b: i128 = -0x8000_0000_0000_0000_0000_0000_0000_0000; |
| 61 | try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885); | 61 | try expect(@divFloor(b, 1_000_000) == -170141183460469231731687303715885); |
| 62 | try expect(@divCeil(b, 1_000_000) == -170141183460469231731687303715884); | ||
| 62 | try expect(a == b); | 63 | try expect(a == b); |
| 63 | } | 64 | } |
| 64 | 65 |
test/behavior/math.zig+89| ... | @@ -488,6 +488,36 @@ fn testIntDivision() !void { | ... | @@ -488,6 +488,36 @@ fn testIntDivision() !void { |
| 488 | try expect(divFloor(i64, -0x80000000, -2) == 0x40000000); | 488 | try expect(divFloor(i64, -0x80000000, -2) == 0x40000000); |
| 489 | try expect(divFloor(i64, -0x40000001, 0x40000000) == -2); | 489 | try expect(divFloor(i64, -0x40000001, 0x40000000) == -2); |
| 490 | 490 | ||
| 491 | try expect(divCeil(i32, 5, 3) == 2); | ||
| 492 | try expect(divCeil(i32, -5, 3) == -1); | ||
| 493 | try expect(divCeil(i32, -0x80000000, -2) == 0x40000000); | ||
| 494 | try expect(divCeil(i32, 0, -0x80000000) == 0); | ||
| 495 | try expect(divCeil(i32, -0x40000001, 0x40000000) == -1); | ||
| 496 | try expect(divCeil(i32, -0x80000000, 1) == -0x80000000); | ||
| 497 | try expect(divCeil(i32, 10, 12) == 1); | ||
| 498 | try expect(divCeil(i32, -14, 12) == -1); | ||
| 499 | try expect(divCeil(i32, -2, 12) == 0); | ||
| 500 | |||
| 501 | try expect(divCeil(u32, 5, 3) == 2); | ||
| 502 | try expect(divCeil(u32, 16, 4) == 4); | ||
| 503 | try expect(divCeil(u32, 0, 100) == 0); | ||
| 504 | try expect(divCeil(u32, maxInt(u32) - 1, 100) == 42949673); | ||
| 505 | |||
| 506 | try expect(divCeil(i64, 5, 3) == 2); | ||
| 507 | try expect(divCeil(i64, -5, 3) == -1); | ||
| 508 | try expect(divCeil(i64, -0x80000000, -2) == 0x40000000); | ||
| 509 | try expect(divCeil(i64, 0, -0x80000000) == 0); | ||
| 510 | try expect(divCeil(i64, -0x40000001, 0x40000000) == -1); | ||
| 511 | try expect(divCeil(i64, -0x80000000, 1) == -0x80000000); | ||
| 512 | try expect(divCeil(i64, 10, 12) == 1); | ||
| 513 | try expect(divCeil(i64, -14, 12) == -1); | ||
| 514 | try expect(divCeil(i64, -2, 12) == 0); | ||
| 515 | |||
| 516 | try expect(divCeil(u64, 5, 3) == 2); | ||
| 517 | try expect(divCeil(u64, 16, 4) == 4); | ||
| 518 | try expect(divCeil(u64, 0, 100) == 0); | ||
| 519 | try expect(divCeil(u64, maxInt(u64) - 1, 10000) == 1844674407370956); | ||
| 520 | |||
| 491 | try expect(divTrunc(i32, 5, 3) == 1); | 521 | try expect(divTrunc(i32, 5, 3) == 1); |
| 492 | try expect(divTrunc(i32, -5, 3) == -1); | 522 | try expect(divTrunc(i32, -5, 3) == -1); |
| 493 | try expect(divTrunc(i32, 9, -10) == 0); | 523 | try expect(divTrunc(i32, 9, -10) == 0); |
| ... | @@ -531,6 +561,24 @@ fn testIntDivision() !void { | ... | @@ -531,6 +561,24 @@ fn testIntDivision() !void { |
| 531 | try expect( | 561 | try expect( |
| 532 | 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, | 562 | 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, |
| 533 | ); | 563 | ); |
| 564 | try expect( | ||
| 565 | @divFloor(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -3, | ||
| 566 | ); | ||
| 567 | try expect( | ||
| 568 | @divFloor(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -3, | ||
| 569 | ); | ||
| 570 | try expect( | ||
| 571 | @divFloor(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, | ||
| 572 | ); | ||
| 573 | try expect( | ||
| 574 | @divCeil(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, | ||
| 575 | ); | ||
| 576 | try expect( | ||
| 577 | @divCeil(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, | ||
| 578 | ); | ||
| 579 | try expect( | ||
| 580 | @divCeil(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 3, | ||
| 581 | ); | ||
| 534 | try expect( | 582 | try expect( |
| 535 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, | 583 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, |
| 536 | ); | 584 | ); |
| ... | @@ -559,6 +607,13 @@ fn testFloatDivision() !void { | ... | @@ -559,6 +607,13 @@ fn testFloatDivision() !void { |
| 559 | try expect(divFloor(f16, -43.0, 12.0) == -4.0); | 607 | try expect(divFloor(f16, -43.0, 12.0) == -4.0); |
| 560 | try expect(divFloor(f64, -90.0, -9.0) == 10.0); | 608 | try expect(divFloor(f64, -90.0, -9.0) == 10.0); |
| 561 | 609 | ||
| 610 | try expect(divCeil(f32, 5.0, 3.0) == 2.0); | ||
| 611 | try expect(divCeil(f32, -5.0, 3.0) == -1.0); | ||
| 612 | try expect(divCeil(f32, 56.0, 9.0) == 7.0); | ||
| 613 | try expect(divCeil(f32, 1053.0, -41.0) == -25.0); | ||
| 614 | try expect(divCeil(f16, -43.0, 12.0) == -3.0); | ||
| 615 | try expect(divCeil(f64, -90.0, -9.0) == 10.0); | ||
| 616 | |||
| 562 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); | 617 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); |
| 563 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); | 618 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); |
| 564 | try expect(divTrunc(f32, 9.0, -10.0) == 0.0); | 619 | try expect(divTrunc(f32, 9.0, -10.0) == 0.0); |
| ... | @@ -607,6 +662,8 @@ fn testDivisionFP16() !void { | ... | @@ -607,6 +662,8 @@ fn testDivisionFP16() !void { |
| 607 | 662 | ||
| 608 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); | 663 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); |
| 609 | try expect(divFloor(f16, -5.0, 3.0) == -2.0); | 664 | try expect(divFloor(f16, -5.0, 3.0) == -2.0); |
| 665 | try expect(divCeil(f16, 5.0, 3.0) == 2.0); | ||
| 666 | try expect(divCeil(f16, -5.0, 3.0) == -1.0); | ||
| 610 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); | 667 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); |
| 611 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); | 668 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); |
| 612 | try expect(divTrunc(f16, 9.0, -10.0) == 0.0); | 669 | try expect(divTrunc(f16, 9.0, -10.0) == 0.0); |
| ... | @@ -622,6 +679,9 @@ fn divExact(comptime T: type, a: T, b: T) T { | ... | @@ -622,6 +679,9 @@ fn divExact(comptime T: type, a: T, b: T) T { |
| 622 | fn divFloor(comptime T: type, a: T, b: T) T { | 679 | fn divFloor(comptime T: type, a: T, b: T) T { |
| 623 | return @divFloor(a, b); | 680 | return @divFloor(a, b); |
| 624 | } | 681 | } |
| 682 | fn divCeil(comptime T: type, a: T, b: T) T { | ||
| 683 | return @divCeil(a, b); | ||
| 684 | } | ||
| 625 | fn divTrunc(comptime T: type, a: T, b: T) T { | 685 | fn divTrunc(comptime T: type, a: T, b: T) T { |
| 626 | return @divTrunc(a, b); | 686 | return @divTrunc(a, b); |
| 627 | } | 687 | } |
| ... | @@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" { | ... | @@ -1846,6 +1906,35 @@ test "@divFloor > 128 bits" { |
| 1846 | try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1); | 1906 | try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1); |
| 1847 | } | 1907 | } |
| 1848 | 1908 | ||
| 1909 | fn testDivCeil(comptime T: type, numerator: T, denominator: T, expected: T) !void { | ||
| 1910 | try expect(@divCeil(numerator, denominator) == expected); | ||
| 1911 | } | ||
| 1912 | |||
| 1913 | test "@divCeil > 128 bits" { | ||
| 1914 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 1915 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 1916 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1917 | |||
| 1918 | try testDivCeil(u140, 0, maxInt(u140), 0); | ||
| 1919 | try testDivCeil(u140, maxInt(u140), maxInt(u140), 1); | ||
| 1920 | try testDivCeil(u140, maxInt(u140), 2, maxInt(u140) / 2 + 1); | ||
| 1921 | try testDivCeil(u140, (1 << 139) + 5, 1 << 70, (1 << 69) + 1); | ||
| 1922 | try testDivCeil(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 2); | ||
| 1923 | try testDivCeil(u200, 123, 1 << 100, 1); | ||
| 1924 | try testDivCeil(u200, 1 << 120, 1 << 60, 1 << 60); | ||
| 1925 | try testDivCeil(u200, maxInt(u200), 1 << 100, 1 << 100); | ||
| 1926 | |||
| 1927 | try testDivCeil(i140, 0, maxInt(i140), 0); | ||
| 1928 | try testDivCeil(i140, maxInt(i140), maxInt(i140), 1); | ||
| 1929 | try testDivCeil(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50)); | ||
| 1930 | try testDivCeil(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50)); | ||
| 1931 | try testDivCeil(i140, -((1 << 100) + 1), -(1 << 50), (1 << 50) + 1); | ||
| 1932 | try testDivCeil(i200, -3, 2, -1); | ||
| 1933 | try testDivCeil(i200, minInt(i200), 1, minInt(i200)); | ||
| 1934 | try testDivCeil(i200, minInt(i200), -2, 1 << 198); | ||
| 1935 | try testDivCeil(i200, maxInt(i200), 2, 1 << 198); | ||
| 1936 | } | ||
| 1937 | |||
| 1849 | fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void { | 1938 | fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void { |
| 1850 | try expect(@divTrunc(numerator, denominator) == expected); | 1939 | try expect(@divTrunc(numerator, denominator) == expected); |
| 1851 | } | 1940 | } |
test/behavior/vector.zig+35-1| ... | @@ -510,8 +510,37 @@ test "vector division operators" { | ... | @@ -510,8 +510,37 @@ test "vector division operators" { |
| 510 | inline for (@as([4]T, d2), 0..) |v, i| { | 510 | inline for (@as([4]T, d2), 0..) |v, i| { |
| 511 | try expect(@divFloor(x[i], y[i]) == v); | 511 | try expect(@divFloor(x[i], y[i]) == v); |
| 512 | } | 512 | } |
| 513 | const d3 = @divTrunc(x, y); | 513 | const d3 = @divCeil(x, y); |
| 514 | inline for (@as([4]T, d3), 0..) |v, i| { | 514 | inline for (@as([4]T, d3), 0..) |v, i| { |
| 515 | try expect(@divCeil(x[i], y[i]) == v); | ||
| 516 | } | ||
| 517 | const d4 = @divTrunc(x, y); | ||
| 518 | inline for (@as([4]T, d4), 0..) |v, i| { | ||
| 519 | try expect(@divTrunc(x[i], y[i]) == v); | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | fn doTheTestDivNoExact(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void { | ||
| 524 | const is_signed_int = switch (@typeInfo(T)) { | ||
| 525 | .int => |info| info.signedness == .signed, | ||
| 526 | else => false, | ||
| 527 | }; | ||
| 528 | if (!is_signed_int) { | ||
| 529 | const d0 = x / y; | ||
| 530 | inline for (@as([4]T, d0), 0..) |v, i| { | ||
| 531 | try expect(x[i] / y[i] == v); | ||
| 532 | } | ||
| 533 | } | ||
| 534 | const d2 = @divFloor(x, y); | ||
| 535 | inline for (@as([4]T, d2), 0..) |v, i| { | ||
| 536 | try expect(@divFloor(x[i], y[i]) == v); | ||
| 537 | } | ||
| 538 | const d3 = @divCeil(x, y); | ||
| 539 | inline for (@as([4]T, d3), 0..) |v, i| { | ||
| 540 | try expect(@divCeil(x[i], y[i]) == v); | ||
| 541 | } | ||
| 542 | const d4 = @divTrunc(x, y); | ||
| 543 | inline for (@as([4]T, d4), 0..) |v, i| { | ||
| 515 | try expect(@divTrunc(x[i], y[i]) == v); | 544 | try expect(@divTrunc(x[i], y[i]) == v); |
| 516 | } | 545 | } |
| 517 | } | 546 | } |
| ... | @@ -566,6 +595,9 @@ test "vector division operators" { | ... | @@ -566,6 +595,9 @@ test "vector division operators" { |
| 566 | try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); | 595 | try doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); |
| 567 | try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); | 596 | try doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); |
| 568 | try doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 }); | 597 | try doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 }); |
| 598 | |||
| 599 | try doTheTestDivNoExact(u64, [4]u64{ 4, 5, 6, 7 }, [4]u64{ 4, 4, 4, 4 }); | ||
| 600 | try doTheTestDivNoExact(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 3, 3, -3, -3 }); | ||
| 569 | } | 601 | } |
| 570 | }; | 602 | }; |
| 571 | 603 | ||
| ... | @@ -1318,11 +1350,13 @@ test "zero divisor" { | ... | @@ -1318,11 +1350,13 @@ test "zero divisor" { |
| 1318 | const v2 = @divExact(zeros, ones); | 1350 | const v2 = @divExact(zeros, ones); |
| 1319 | const v3 = @divTrunc(zeros, ones); | 1351 | const v3 = @divTrunc(zeros, ones); |
| 1320 | const v4 = @divFloor(zeros, ones); | 1352 | const v4 = @divFloor(zeros, ones); |
| 1353 | const v5 = @divCeil(zeros, ones); | ||
| 1321 | 1354 | ||
| 1322 | _ = v1[0]; | 1355 | _ = v1[0]; |
| 1323 | _ = v2[0]; | 1356 | _ = v2[0]; |
| 1324 | _ = v3[0]; | 1357 | _ = v3[0]; |
| 1325 | _ = v4[0]; | 1358 | _ = v4[0]; |
| 1359 | _ = v5[0]; | ||
| 1326 | } | 1360 | } |
| 1327 | 1361 | ||
| 1328 | test "zero multiplicand" { | 1362 | test "zero multiplicand" { |
test/behavior/x86_64/binary.zig+21| ... | @@ -5279,6 +5279,27 @@ test divFloorOptimized { | ... | @@ -5279,6 +5279,27 @@ test divFloorOptimized { |
| 5279 | try test_div_floor_optimized.testFloatVectors(); | 5279 | try test_div_floor_optimized.testFloatVectors(); |
| 5280 | } | 5280 | } |
| 5281 | 5281 | ||
| 5282 | inline fn divCeilUnoptimized(comptime Type: type, lhs: Type, rhs: Type) Type { | ||
| 5283 | return @divCeil(lhs, rhs); | ||
| 5284 | } | ||
| 5285 | test divCeilUnoptimized { | ||
| 5286 | const test_div_ceil_unoptimized = binary(divCeilUnoptimized, .{ .compare = .approx_int }); | ||
| 5287 | try test_div_ceil_unoptimized.testInts(); | ||
| 5288 | try test_div_ceil_unoptimized.testIntVectors(); | ||
| 5289 | try test_div_ceil_unoptimized.testFloats(); | ||
| 5290 | try test_div_ceil_unoptimized.testFloatVectors(); | ||
| 5291 | } | ||
| 5292 | |||
| 5293 | inline fn divCeilOptimized(comptime Type: type, lhs: Type, rhs: Type) Type { | ||
| 5294 | @setFloatMode(.optimized); | ||
| 5295 | return @divCeil(lhs, select(@abs(rhs) > splat(Type, 0.0), rhs, splat(Type, 1.0))); | ||
| 5296 | } | ||
| 5297 | test divCeilOptimized { | ||
| 5298 | const test_div_ceil_optimized = binary(divCeilOptimized, .{ .compare = .approx_int }); | ||
| 5299 | try test_div_ceil_optimized.testFloats(); | ||
| 5300 | try test_div_ceil_optimized.testFloatVectors(); | ||
| 5301 | } | ||
| 5302 | |||
| 5282 | inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type { | 5303 | inline fn rem(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5283 | return @rem(lhs, rhs); | 5304 | return @rem(lhs, rhs); |
| 5284 | } | 5305 | } |
test/cases/compile_errors/signed_integer_division.zig+1-1| ... | @@ -4,4 +4,4 @@ export fn foo(a: i32, b: i32) i32 { | ... | @@ -4,4 +4,4 @@ export fn foo(a: i32, b: i32) i32 { |
| 4 | 4 | ||
| 5 | // error | 5 | // error |
| 6 | // | 6 | // |
| 7 | // :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact | 7 | // :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact |