Skip to content

Commit 6af0e57

Browse files
authored
Merge pull request #18 from antoyo/fix-u128-clz
Fix 128-bit integers and count leading zeroes intrinsic
2 parents 17102dc + f7b9300 commit 6af0e57

File tree

3 files changed

+119
-36
lines changed

3 files changed

+119
-36
lines changed

gcc-test-backend/src/main.rs

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
1-
#![feature(core_intrinsics)]
2-
3-
fn i128_to_u64(u: i128) -> Option<u64> {
4-
let min = u64::MIN as i128;
5-
//let max = u64::MAX as i128;
6-
let max = 18446744073709551612_i128;
7-
//println!("{} < {} => {}", u, min, u < min);
8-
//println!("max: {:b}", u64::MAX);
9-
println!("max: {:b}", max);
10-
println!("max: {}", max);
11-
//println!("{}", u < min);
12-
//println!("{}", u > max);
13-
if u < min || u > max {
14-
None
15-
} else {
16-
Some(u as u64)
17-
}
18-
}
1+
#![feature(const_option)]
2+
3+
use std::num::{NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize};
194

205
fn main() {
216
/*test_float!(f64, f64, f64::INFINITY, f64::NEG_INFINITY, f64::NAN);
@@ -58,21 +43,6 @@ fn main() {
5843
}
5944
println!("{}", 9.4f64);*/
6045

61-
let mut value = 0;
62-
let res = unsafe { std::intrinsics::atomic_cxchg(&mut value, 0, 1) };
63-
println!("{:?}", res);
64-
let res = unsafe { std::intrinsics::atomic_cxchg(&mut value, 0, 1) };
65-
println!("{:?}", res);
66-
67-
use std::sync::atomic::{AtomicBool, Ordering};
68-
69-
let a = AtomicBool::new(false);
70-
assert_eq!(a.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst), Ok(false));
71-
assert_eq!(a.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst), Err(true));
72-
73-
a.store(false, Ordering::SeqCst);
74-
assert_eq!(a.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst), Ok(false));
75-
7646
// FIXME: the code seems to be the same when using an integer, but somehow, it doesn't work for
7747
// a float. Could it be related to the fact that floating-points use different registers?
7848

@@ -160,4 +130,71 @@ fn main() {
160130
t_min as u64
161131
);*/
162132
*/
133+
134+
assert_eq!(NonZeroU8::new(1).unwrap().leading_zeros(), 7);
135+
assert_eq!(NonZeroI8::new(1).unwrap().leading_zeros(), 7);
136+
assert_eq!(NonZeroU16::new(1).unwrap().leading_zeros(), 15);
137+
assert_eq!(NonZeroI16::new(1).unwrap().leading_zeros(), 15);
138+
assert_eq!(NonZeroU32::new(1).unwrap().leading_zeros(), 31);
139+
assert_eq!(NonZeroI32::new(1).unwrap().leading_zeros(), 31);
140+
assert_eq!(NonZeroU64::new(1).unwrap().leading_zeros(), 63);
141+
assert_eq!(NonZeroI64::new(1).unwrap().leading_zeros(), 63);
142+
assert_eq!(NonZeroU128::new(1).unwrap().leading_zeros(), 127);
143+
assert_eq!(NonZeroI128::new(1).unwrap().leading_zeros(), 127);
144+
assert_eq!(NonZeroUsize::new(1).unwrap().leading_zeros(), usize::BITS - 1);
145+
assert_eq!(NonZeroIsize::new(1).unwrap().leading_zeros(), usize::BITS - 1);
146+
147+
assert_eq!(NonZeroU8::new(u8::MAX >> 2).unwrap().leading_zeros(), 2);
148+
assert_eq!(NonZeroI8::new((u8::MAX >> 2) as i8).unwrap().leading_zeros(), 2);
149+
assert_eq!(NonZeroU16::new(u16::MAX >> 2).unwrap().leading_zeros(), 2);
150+
assert_eq!(NonZeroI16::new((u16::MAX >> 2) as i16).unwrap().leading_zeros(), 2);
151+
assert_eq!(NonZeroU32::new(u32::MAX >> 2).unwrap().leading_zeros(), 2);
152+
assert_eq!(NonZeroI32::new((u32::MAX >> 2) as i32).unwrap().leading_zeros(), 2);
153+
assert_eq!(NonZeroU64::new(u64::MAX >> 2).unwrap().leading_zeros(), 2);
154+
assert_eq!(NonZeroI64::new((u64::MAX >> 2) as i64).unwrap().leading_zeros(), 2);
155+
156+
/*
157+
//let mut num = u128::MAX >> 20;
158+
//let mut num = u128::MAX;
159+
#[inline(never)]
160+
fn two() -> u128 {
161+
2
162+
}
163+
164+
//let mut num = 340282366920938463463374607431768211455_u128 >> two();
165+
let mut num = 340282366920938463463374607431768211455_u128 >> 2;
166+
//let mut num = 340282366920938463463374607431768211455_u128;
167+
//let mut num = 10_u128 >> 2;
168+
const MASK: u128 = 0x80000000000000000000000000000000;
169+
for _ in 0..128 {
170+
if num & MASK == MASK {
171+
print!("1");
172+
}
173+
else {
174+
print!("0");
175+
}
176+
num <<= 1;
177+
}
178+
println!();
179+
*/
180+
assert_eq!(NonZeroU128::new(u128::MAX >> 2).unwrap().leading_zeros(), 2);
181+
assert_eq!(NonZeroI128::new((u128::MAX >> 2) as i128).unwrap().leading_zeros(), 2);
182+
assert_eq!(NonZeroUsize::new(usize::MAX >> 2).unwrap().leading_zeros(), 2);
183+
assert_eq!(NonZeroIsize::new((usize::MAX >> 2) as isize).unwrap().leading_zeros(), 2);
184+
185+
assert_eq!(NonZeroU8::new(u8::MAX).unwrap().leading_zeros(), 0);
186+
assert_eq!(NonZeroI8::new(-1i8).unwrap().leading_zeros(), 0);
187+
assert_eq!(NonZeroU16::new(u16::MAX).unwrap().leading_zeros(), 0);
188+
assert_eq!(NonZeroI16::new(-1i16).unwrap().leading_zeros(), 0);
189+
assert_eq!(NonZeroU32::new(u32::MAX).unwrap().leading_zeros(), 0);
190+
assert_eq!(NonZeroI32::new(-1i32).unwrap().leading_zeros(), 0);
191+
assert_eq!(NonZeroU64::new(u64::MAX).unwrap().leading_zeros(), 0);
192+
assert_eq!(NonZeroI64::new(-1i64).unwrap().leading_zeros(), 0);
193+
assert_eq!(NonZeroU128::new(u128::MAX).unwrap().leading_zeros(), 0);
194+
assert_eq!(NonZeroI128::new(-1i128).unwrap().leading_zeros(), 0);
195+
assert_eq!(NonZeroUsize::new(usize::MAX).unwrap().leading_zeros(), 0);
196+
assert_eq!(NonZeroIsize::new(-1isize).unwrap().leading_zeros(), 0);
197+
198+
const LEADING_ZEROS: u32 = NonZeroU16::new(1).unwrap().leading_zeros();
199+
assert_eq!(LEADING_ZEROS, 15);
163200
}

src/common.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,12 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
136136
}
137137

138138
// FIXME: use a new function new_rvalue_from_unsigned_long()?
139-
let low = self.context.new_rvalue_from_long(typ, num as u64 as i64);
139+
let low = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
140140
let high = self.context.new_rvalue_from_long(typ, (num >> 64) as u64 as i64);
141141

142142
let sixty_four = self.context.new_rvalue_from_long(typ, 64);
143143

144-
(high << sixty_four) | low
144+
(high << sixty_four) | self.context.new_cast(None, low, typ)
145145

146146
/*unsafe {
147147
let words = [u as u64, (u >> 64) as u64];

src/intrinsic.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
233233
then_block.add_assignment(None, result, zero_result);
234234
then_block.end_with_jump(None, after_block);
235235

236+
// NOTE: since jumps were added in a place
237+
// count_leading_zeroes() does not expect, the current blocks
238+
// in the state need to be updated.
239+
*self.current_block.borrow_mut() = Some(else_block);
240+
self.block = Some(else_block);
241+
236242
let zeros =
237243
match name {
238244
sym::ctlz => self.count_leading_zeroes(width, arg),
@@ -732,6 +738,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
732738
}
733739

734740
fn count_leading_zeroes(&self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
741+
// TODO: use width?
735742
let arg_type = arg.get_type();
736743
let count_leading_zeroes =
737744
if arg_type.is_uint(&self.cx) {
@@ -743,8 +750,47 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
743750
else if arg_type.is_ulonglong(&self.cx) {
744751
"__builtin_clzll"
745752
}
753+
else if width == 128 {
754+
// Algorithm from: https://stackoverflow.com/a/28433850/389119
755+
let array_type = self.context.new_array_type(None, arg_type, 3);
756+
let result = self.current_func()
757+
.new_local(None, array_type, "count_loading_zeroes_results");
758+
759+
let sixty_four = self.context.new_rvalue_from_long(arg_type, 64);
760+
let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type);
761+
let low = self.context.new_cast(None, arg, self.u64_type);
762+
763+
let zero = self.context.new_rvalue_zero(self.usize_type);
764+
let one = self.context.new_rvalue_one(self.usize_type);
765+
let two = self.context.new_rvalue_from_long(self.usize_type, 2);
766+
767+
let clzll = self.context.get_builtin_function("__builtin_clzll");
768+
769+
let first_elem = self.context.new_array_access(None, result, zero);
770+
let first_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[high]), arg_type);
771+
self.llbb()
772+
.add_assignment(None, first_elem, first_value);
773+
774+
let second_elem = self.context.new_array_access(None, result, one);
775+
let second_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[low]), arg_type) + sixty_four;
776+
self.llbb()
777+
.add_assignment(None, second_elem, second_value);
778+
779+
let third_elem = self.context.new_array_access(None, result, two);
780+
let third_value = self.context.new_rvalue_from_long(arg_type, 128);
781+
self.llbb()
782+
.add_assignment(None, third_elem, third_value);
783+
784+
let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high);
785+
let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low);
786+
let not_low_and_not_high = not_low & not_high;
787+
let index = not_high + not_low_and_not_high;
788+
789+
let res = self.context.new_array_access(None, result, index);
790+
791+
return self.context.new_cast(None, res, arg_type);
792+
}
746793
else {
747-
// TODO: implement for 128-bit integers.
748794
let count_leading_zeroes = self.context.get_builtin_function("__builtin_clz");
749795
let arg = self.context.new_cast(None, arg, self.uint_type);
750796
let diff = self.int_width(self.uint_type) - self.int_width(arg_type);

0 commit comments

Comments
 (0)