-
Notifications
You must be signed in to change notification settings - Fork 232
port udivmoddi4 and __aeabi_uldivmod #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
9505646
732f1e2
39ede7e
cc9d3e8
0915e15
a2fc4da
d6be95a
a84579d
3ff25d9
1c22269
eb20684
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ | |
authors = ["Jorge Aparicio <[email protected]>"] | ||
name = "rustc_builtins" | ||
version = "0.1.0" | ||
|
||
[dev-dependencies] | ||
quickcheck = "0.3.1" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
use {Int, LargeInt, U64}; | ||
|
||
/// Returns `n / d` | ||
#[no_mangle] | ||
pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { | ||
let u32_bits = u32::bits() as u32; | ||
|
||
// Special cases | ||
if d == 0 { | ||
return 0; // ?! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
if n == 0 { | ||
return 0; | ||
} | ||
|
||
let mut sr = d.leading_zeros().wrapping_sub(n.leading_zeros()); | ||
|
||
// d > n | ||
if sr > u32_bits - 1 { | ||
return 0; | ||
} | ||
|
||
// d == 1 | ||
if sr == u32_bits - 1 { | ||
return n; | ||
} | ||
|
||
sr = sr + 1; | ||
|
||
// 1 <= sr <= u32_bits - 1 | ||
let mut q = n << (u32_bits - sr); | ||
let mut r = n >> sr; | ||
|
||
let mut carry = 0; | ||
for _ in 0..sr { | ||
// r:q = ((r:q) << 1) | carry | ||
r = (r << 1) | (q >> (u32_bits - 1)); | ||
q = (q << 1) | carry; | ||
|
||
// carry = 0; | ||
// if r > d { | ||
// r -= d; | ||
// carry = 1; | ||
// } | ||
|
||
let s = (d.wrapping_sub(r).wrapping_sub(1)) as i32 >> (u32_bits - 1); | ||
carry = (s & 1) as u32; | ||
r -= d & s as u32; | ||
} | ||
|
||
q = (q << 1) | carry; | ||
q | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just return |
||
} | ||
|
||
/// Returns `n / d` and sets `*rem = n % d` | ||
#[no_mangle] | ||
pub extern "C" fn __udivmodsi4(a: u32, b: u32, rem: Option<&mut u32>) -> u32 { | ||
let d = __udivsi3(a, b); | ||
if let Some(rem) = rem { | ||
*rem = a - (d * b); | ||
} | ||
return d; | ||
} | ||
|
||
/// Returns `n / d` and sets `*rem = n % d` | ||
#[no_mangle] | ||
pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { | ||
let u32_bits = u32::bits() as u32; | ||
let u64_bits = u64::bits() as u32; | ||
|
||
// NOTE X is unknown, K != 0 | ||
if n.high() == 0 { | ||
if d.high() == 0 { | ||
// 0 X | ||
// --- | ||
// 0 X | ||
|
||
if let Some(rem) = rem { | ||
*rem = u64::from(n.low() % d.low()); | ||
} | ||
return u64::from(n.low() / d.low()); | ||
} else | ||
// d.high() != 0 | ||
{ | ||
// 0 X | ||
// --- | ||
// K X | ||
|
||
if let Some(rem) = rem { | ||
*rem = u64::from(n.low()); | ||
} | ||
return 0; | ||
}; | ||
} | ||
|
||
let mut sr; | ||
let mut q = U64 { low: 0, high: 0 }; | ||
let mut r = U64 { low: 0, high: 0 }; | ||
|
||
// n.high() != 0 | ||
if d.low() == 0 { | ||
if d.high() == 0 { | ||
// K X | ||
// --- | ||
// 0 0 | ||
|
||
// NOTE copied verbatim from compiler-rt. This probably lets the intrinsic decide how to | ||
// handle the division by zero (SIGFPE, 0, etc.). But this part shouldn't be reachable | ||
// from safe code. | ||
if let Some(rem) = rem { | ||
*rem = u64::from(n.high() % d.low()); | ||
} | ||
return u64::from(n.high() / d.low()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I think we could just go with |
||
} | ||
|
||
// d.high() != 0 | ||
if n.low() == 0 { | ||
// K 0 | ||
// --- | ||
// K 0 | ||
|
||
if let Some(rem) = rem { | ||
*rem = U64 { | ||
low: 0, | ||
high: n.high() % d.high(), | ||
}[..]; | ||
} | ||
return u64::from(n.high() / d.high()); | ||
} | ||
|
||
// n.low() != 0 | ||
// K K | ||
// --- | ||
// K 0 | ||
|
||
if d.high().is_power_of_two() { | ||
if let Some(rem) = rem { | ||
*rem = U64 { | ||
low: n.low(), | ||
high: n.high() & (d.high() - 1), | ||
}[..]; | ||
} | ||
|
||
return u64::from(n.high() >> d.high().trailing_zeros()); | ||
} | ||
|
||
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); | ||
|
||
// D > N | ||
if sr > u32_bits - 2 { | ||
if let Some(rem) = rem { | ||
*rem = n; | ||
} | ||
return 0; | ||
} | ||
|
||
sr = sr + 1; | ||
|
||
// 1 <= sr <= u32_bits - 1 | ||
// q = n << (u64_bits - sr); | ||
q.low = 0; | ||
q.high = n.low() << (u32_bits - sr); | ||
// r = n >> sr | ||
r.high = n.high() >> sr; | ||
r.low = (n.high() << (u32_bits - sr)) | (n.low() >> sr); | ||
} else | ||
// d.low() != 0 | ||
{ | ||
if d.high() == 0 { | ||
// K X | ||
// --- | ||
// 0 K | ||
if d.low().is_power_of_two() { | ||
if let Some(rem) = rem { | ||
*rem = u64::from(n.low() & (d.low() - 1)); | ||
} | ||
|
||
if d.low() == 1 { | ||
return n; | ||
} else { | ||
let sr = d.low().trailing_zeros(); | ||
return U64 { | ||
low: (n.high() << (u32_bits - sr)) | (n.low() >> sr), | ||
high: n.high() >> sr, | ||
}[..]; | ||
}; | ||
} | ||
|
||
sr = 1 + u32_bits + d.low().leading_zeros() - n.high().leading_zeros(); | ||
|
||
// 2 <= sr <= u64_bits - 1 | ||
// q = n << (u64_bits - sr) | ||
// r = n >> sr; | ||
if sr == u32_bits { | ||
q.low = 0; | ||
q.high = n.low(); | ||
r.high = 0; | ||
r.low = n.high(); | ||
} else if sr < u32_bits | ||
// 2 <= sr <= u32_bits - 1 | ||
{ | ||
q.low = 0; | ||
q.high = n.low() << (u32_bits - sr); | ||
r.high = n.high() >> sr; | ||
r.low = (n.high() << (u32_bits - sr)) | (n.low() >> sr); | ||
} else | ||
// u32_bits + 1 <= sr <= u64_bits - 1 | ||
{ | ||
q.low = n.low() << (u64_bits - sr); | ||
q.high = (n.high() << (u64_bits - sr)) | (n.low() >> (sr - u32_bits)); | ||
r.high = 0; | ||
r.low = n.high() >> (sr - u32_bits); | ||
} | ||
|
||
} else | ||
// d.high() != 0 | ||
{ | ||
// K X | ||
// --- | ||
// K K | ||
|
||
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); | ||
|
||
// D > N | ||
if sr > u32_bits - 1 { | ||
if let Some(rem) = rem { | ||
*rem = n; | ||
return 0; | ||
} | ||
} | ||
|
||
sr += 1; | ||
|
||
// 1 <= sr <= u32_bits | ||
// q = n << (u64_bits - sr) | ||
q.low = 0; | ||
if sr == u32_bits { | ||
q.high = n.low(); | ||
r.high = 0; | ||
r.low = n.high(); | ||
} else { | ||
q.high = n.low() << (u32_bits - sr); | ||
r.high = n.high() >> sr; | ||
r.low = (n.high() << (u32_bits - sr)) | (n.low() >> sr); | ||
} | ||
} | ||
} | ||
|
||
// Not a special case | ||
// q and r are initialized with | ||
// q = n << (u64_bits - sr) | ||
// r = n >> sr | ||
// 1 <= sr <= u64_bits - 1 | ||
let mut carry = 0; | ||
|
||
for _ in 0..sr { | ||
// r:q = ((r:q) << 1) | carry | ||
r[..] = (r[..] << 1) | (q[..] >> 63); | ||
q[..] = (q[..] << 1) | carry as u64; | ||
|
||
// carry = 0 | ||
// if r >= d { | ||
// r -= d; | ||
// carry = 1; | ||
// } | ||
|
||
let s = (d.wrapping_sub(r[..]).wrapping_sub(1)) as i64 >> (u64_bits - 1); | ||
carry = (s & 1) as u32; | ||
r[..] -= d & s as u64; | ||
} | ||
|
||
q[..] = (q[..] << 1) | carry as u64; | ||
if let Some(rem) = rem { | ||
*rem = r[..]; | ||
} | ||
q[..] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment explaining why an asm implementation was necessary (non-standard calling convention) would be nice here.