| 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); |
| 3 | const testing = std.testing; |
| 4 | const compiler_rt = @import("../compiler_rt.zig"); |
| 5 | const symbol = compiler_rt.symbol; |
| 6 | const native_endian = builtin.cpu.arch.endian(); |
| 7 | |
| 8 | comptime { |
| 9 | symbol(&__mulsi3, "__mulsi3"); |
| 10 | if (compiler_rt.want_aeabi) { |
| 11 | symbol(&__aeabi_lmul, "__aeabi_lmul"); |
| 12 | } else { |
| 13 | symbol(&__muldi3, "__muldi3"); |
| 14 | } |
| 15 | symbol(&__multi3, "__multi3"); |
| 16 | } |
| 17 | |
| 18 | pub fn __mulsi3(a: i32, b: i32) callconv(.c) i32 { |
| 19 | var ua: u32 = @bitCast(a); |
| 20 | var ub: u32 = @bitCast(b); |
| 21 | var r: u32 = 0; |
| 22 | |
| 23 | while (ua > 0) { |
| 24 | if ((ua & 1) != 0) r +%= ub; |
| 25 | ua >>= 1; |
| 26 | ub <<= 1; |
| 27 | } |
| 28 | |
| 29 | return @bitCast(r); |
| 30 | } |
| 31 | |
| 32 | pub fn __muldi3(a: i64, b: i64) callconv(.c) i64 { |
| 33 | return mulX(i64, a, b); |
| 34 | } |
| 35 | |
| 36 | fn __aeabi_lmul(a: i64, b: i64) callconv(.{ .arm_aapcs = .{} }) i64 { |
| 37 | return mulX(i64, a, b); |
| 38 | } |
| 39 | |
| 40 | inline fn mulX(comptime T: type, a: T, b: T) T { |
| 41 | const word_t = compiler_rt.HalveInt(T, false); |
| 42 | const x = word_t{ .all = a }; |
| 43 | const y = word_t{ .all = b }; |
| 44 | var r = switch (T) { |
| 45 | i64, i128 => word_t{ .all = muldXi(word_t.HalfT, x.s.low, y.s.low) }, |
| 46 | else => unreachable, |
| 47 | }; |
| 48 | r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; |
| 49 | return r.all; |
| 50 | } |
| 51 | |
| 52 | fn DoubleInt(comptime T: type) type { |
| 53 | return switch (T) { |
| 54 | u32 => i64, |
| 55 | u64 => i128, |
| 56 | i32 => i64, |
| 57 | i64 => i128, |
| 58 | else => unreachable, |
| 59 | }; |
| 60 | } |
| 61 | |
| 62 | pub fn muldXi(comptime T: type, a: T, b: T) DoubleInt(T) { |
| 63 | const DT = DoubleInt(T); |
| 64 | const word_t = compiler_rt.HalveInt(DT, false); |
| 65 | const bits_in_word_2 = @sizeOf(T) * 8 / 2; |
| 66 | const lower_mask = (~@as(T, 0)) >> bits_in_word_2; |
| 67 | |
| 68 | var r: word_t = undefined; |
| 69 | r.s.low = (a & lower_mask) *% (b & lower_mask); |
| 70 | var t: T = r.s.low >> bits_in_word_2; |
| 71 | r.s.low &= lower_mask; |
| 72 | t += (a >> bits_in_word_2) *% (b & lower_mask); |
| 73 | r.s.low +%= (t & lower_mask) << bits_in_word_2; |
| 74 | r.s.high = t >> bits_in_word_2; |
| 75 | t = r.s.low >> bits_in_word_2; |
| 76 | r.s.low &= lower_mask; |
| 77 | t +%= (b >> bits_in_word_2) *% (a & lower_mask); |
| 78 | r.s.low +%= (t & lower_mask) << bits_in_word_2; |
| 79 | r.s.high +%= t >> bits_in_word_2; |
| 80 | r.s.high +%= (a >> bits_in_word_2) *% (b >> bits_in_word_2); |
| 81 | return r.all; |
| 82 | } |
| 83 | |
| 84 | pub fn __multi3(a: i128, b: i128) callconv(.c) i128 { |
| 85 | return mulX(i128, a, b); |
| 86 | } |
| 87 | |
| 88 | test { |
| 89 | _ = @import("mulXi3_test.zig"); |
| 90 | } |