| 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); |
| 3 | const math = std.math; |
| 4 | const compiler_rt = @import("../compiler_rt.zig"); |
| 5 | const symbol = compiler_rt.symbol; |
| 6 | |
| 7 | comptime { |
| 8 | symbol(&__mulosi4, "__mulosi4"); |
| 9 | symbol(&__mulodi4, "__mulodi4"); |
| 10 | symbol(&__muloti4, "__muloti4"); |
| 11 | } |
| 12 | |
| 13 | // mulo - multiplication overflow |
| 14 | // * return a*%b. |
| 15 | // * return if a*b overflows => 1 else => 0 |
| 16 | // - muloXi4_genericSmall as default |
| 17 | // - muloXi4_genericFast for 2*bitsize <= usize |
| 18 | |
| 19 | inline fn muloXi4_genericSmall(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { |
| 20 | overflow.* = 0; |
| 21 | const min = math.minInt(ST); |
| 22 | const res: ST = if (ST == i128 and builtin.target.cpu.arch.isWasm()) res: { |
| 23 | // Despite compiler-rt being built with `-fno-builtin`, LLVM still converts this function to |
| 24 | // a call to `__muloti4` on WASM. This is an upstream bug: circumvent it by directly calling |
| 25 | // the "lower-level" compiler-rt routine for this wrapping multiplication. |
| 26 | break :res @import("mulXi3.zig").__multi3(a, b); |
| 27 | } else a *% b; |
| 28 | // Hacker's Delight section Overflow subsection Multiplication |
| 29 | // case a=-2^{31}, b=-1 problem, because |
| 30 | // on some machines a*b = -2^{31} with overflow |
| 31 | // Then -2^{31}/-1 overflows and any result is possible. |
| 32 | // => check with a<0 and b=-2^{31} |
| 33 | if ((a < 0 and b == min) or (a != 0 and @divTrunc(res, a) != b)) |
| 34 | overflow.* = 1; |
| 35 | return res; |
| 36 | } |
| 37 | |
| 38 | inline fn muloXi4_genericFast(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { |
| 39 | overflow.* = 0; |
| 40 | const EST = switch (ST) { |
| 41 | i32 => i64, |
| 42 | i64 => i128, |
| 43 | i128 => i256, |
| 44 | else => unreachable, |
| 45 | }; |
| 46 | const min = math.minInt(ST); |
| 47 | const max = math.maxInt(ST); |
| 48 | const res: EST = @as(EST, a) * @as(EST, b); |
| 49 | //invariant: -2^{bitwidth(EST)} < res < 2^{bitwidth(EST)-1} |
| 50 | if (res < min or max < res) |
| 51 | overflow.* = 1; |
| 52 | return @as(ST, @truncate(res)); |
| 53 | } |
| 54 | |
| 55 | pub fn __mulosi4(a: i32, b: i32, overflow: *c_int) callconv(.c) i32 { |
| 56 | if (2 * @bitSizeOf(i32) <= @bitSizeOf(usize)) { |
| 57 | return muloXi4_genericFast(i32, a, b, overflow); |
| 58 | } else { |
| 59 | return muloXi4_genericSmall(i32, a, b, overflow); |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | pub fn __mulodi4(a: i64, b: i64, overflow: *c_int) callconv(.c) i64 { |
| 64 | if (2 * @bitSizeOf(i64) <= @bitSizeOf(usize)) { |
| 65 | return muloXi4_genericFast(i64, a, b, overflow); |
| 66 | } else { |
| 67 | return muloXi4_genericSmall(i64, a, b, overflow); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.c) i128 { |
| 72 | if (2 * @bitSizeOf(i128) <= @bitSizeOf(usize)) { |
| 73 | return muloXi4_genericFast(i128, a, b, overflow); |
| 74 | } else { |
| 75 | return muloXi4_genericSmall(i128, a, b, overflow); |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | test { |
| 80 | _ = @import("mulosi4_test.zig"); |
| 81 | _ = @import("mulodi4_test.zig"); |
| 82 | _ = @import("muloti4_test.zig"); |
| 83 | } |