From 81b24594ce0f610c9508fde9b5b40340d576914a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 21 Dec 2019 19:49:09 +0000 Subject: [PATCH] Add udivmod128.odin --- core/runtime/udivmod128.odin | 180 +++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 core/runtime/udivmod128.odin diff --git a/core/runtime/udivmod128.odin b/core/runtime/udivmod128.odin new file mode 100644 index 000000000..3486dffc2 --- /dev/null +++ b/core/runtime/udivmod128.odin @@ -0,0 +1,180 @@ +package runtime + +@(default_calling_convention="none") +foreign { + @(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 --- + @(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 --- + @(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 --- + @(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 --- +} +_ctz :: proc{ + _ctz_u8, + _ctz_u16, + _ctz_u32, + _ctz_u64, +}; + +@(default_calling_convention="none") +foreign { + @(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 --- + @(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 --- + @(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 --- + @(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 --- +} +_clz :: proc{ + _clz_u8, + _clz_u16, + _clz_u32, + _clz_u64, +}; + + +udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { + n := transmute([2]u64)a; + d := transmute([2]u64)b; + q, r: [2]u64 = ---, ---; + sr: u32 = 0; + + low :: ODIN_ENDIAN == "big" ? 1 : 0; + high :: 1 - low; + U64_BITS :: 8*size_of(u64); + U128_BITS :: 8*size_of(u128); + + // Special Cases + + if n[high] == 0 { + if d[high] == 0 { + if rem != nil { + res := n[low] % d[low]; + rem^ = u128(res); + } + return u128(n[low] / d[low]); + } + + if rem != nil { + rem^ = u128(n[low]); + } + return 0; + } + + if d[low] == 0 { + if d[high] == 0 { + if rem != nil { + rem^ = u128(n[high] % d[low]); + } + return u128(n[high] / d[low]); + } + if n[low] == 0 { + if rem != nil { + r[high] = n[high] % d[high]; + r[low] = 0; + rem^ = transmute(u128)r; + } + return u128(n[high] / d[high]); + } + + if d[high] & (d[high]-1) == 0 { + if rem != nil { + r[low] = n[low]; + r[high] = n[high] & (d[high] - 1); + rem^ = transmute(u128)r; + } + return u128(n[high] >> _ctz(d[high])); + } + + sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high]))); + if sr > U64_BITS - 2 { + if rem != nil { + rem^ = a; + } + return 0; + } + + sr += 1; + + q[low] = 0; + q[high] = n[low] << u64(U64_BITS - sr); + r[high] = n[high] >> sr; + r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr); + } else { + if d[high] == 0 { + if d[low] & (d[low] - 1) == 0 { + if rem != nil { + rem^ = u128(n[low] & (d[low] - 1)); + } + if d[low] == 1 { + return a; + } + sr = u32(_ctz(d[low])); + q[high] = n[high] >> sr; + q[low] = (n[high] << (U64_BITS-sr)) | (n[low] >> sr); + return transmute(u128)q; + } + + sr = 1 + U64_BITS + u32(_clz(d[low])) - u32(_clz(n[high])); + + switch { + case sr == U64_BITS: + q[low] = 0; + q[high] = n[low]; + r[high] = 0; + r[low] = n[high]; + case sr < U64_BITS: + q[low] = 0; + q[high] = n[low] << (U64_BITS - sr); + r[high] = n[high] >> sr; + r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr); + case: + q[low] = n[low] << (U128_BITS - sr); + q[high] = (n[high] << (U128_BITS - sr)) | (n[low] >> (sr - U64_BITS)); + r[high] = 0; + r[low] = n[high] >> (sr - U64_BITS); + } + } else { + sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high]))); + + if sr > U64_BITS - 1 { + if rem != nil { + rem^ = a; + } + return 0; + } + + sr += 1; + + q[low] = 0; + if sr == U64_BITS { + q[high] = n[low]; + r[high] = 0; + r[low] = n[high]; + } else { + r[high] = n[high] >> sr; + r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr); + q[high] = n[low] << (U64_BITS - sr); + } + } + } + + carry: u32 = 0; + r_all: u128 = ---; + + for ; sr > 0; sr -= 1 { + r[high] = (r[high] << 1) | (r[low] >> (U64_BITS - 1)); + r[low] = (r[low] << 1) | (q[high] >> (U64_BITS - 1)); + q[high] = (q[high] << 1) | (q[low] >> (U64_BITS - 1)); + q[low] = (q[low] << 1) | u64(carry); + + r_all = transmute(u128)r; + s := i128(b - r_all - 1) >> (U128_BITS - 1); + carry = u32(s & 1); + r_all -= b & transmute(u128)s; + r = transmute([2]u64)r_all; + } + + q_all := ((transmute(u128)q) << 1) | u128(carry); + if rem != nil { + rem^ = r_all; + } + + return q_all; +}