Skip to content

Fix atomic_cmpxchg #17

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

Merged
merged 1 commit into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 126 additions & 1 deletion gcc-test-backend/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(core_intrinsics)]

fn i128_to_u64(u: i128) -> Option<u64> {
let min = u64::MIN as i128;
//let max = u64::MAX as i128;
Expand All @@ -16,7 +18,129 @@ fn i128_to_u64(u: i128) -> Option<u64> {
}

fn main() {
use std::convert::TryFrom;
/*test_float!(f64, f64, f64::INFINITY, f64::NEG_INFINITY, f64::NAN);
($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr) => {*/

/*assert_eq!((0.0 as f64).min(0.0), 0.0);
assert!((0.0 as f64).min(0.0).is_sign_positive());
assert_eq!((-0.0 as f64).min(-0.0), -0.0);
assert!((-0.0 as f64).min(-0.0).is_sign_negative());
assert_eq!((9.0 as f64).min(9.0), 9.0);
assert_eq!((-9.0 as f64).min(0.0), -9.0);
assert_eq!((0.0 as f64).min(9.0), 0.0);
assert!((0.0 as f64).min(9.0).is_sign_positive());
assert_eq!((-0.0 as f64).min(9.0), -0.0);
assert!((-0.0 as f64).min(9.0).is_sign_negative());*/

//println!("{}", 9);
//println!("{}", 9.0_f32);
//assert_eq!(-9.0_f32, -9 as f32);

//assert_eq!("1", format!("{:.0}", 1.0f64));
//assert_eq!("9", format!("{:.0}", 9.4f64));
//assert_eq!("10", format!("{:.0}", 9.9f64));
//assert_eq!("9.8", format!("{:.1}", 9.849f64));
//assert_eq!("9.9", format!("{:.1}", 9.851f64));
//assert_eq!("1", format!("{:.0}", 0.5f64));

//assert_eq!(2.3f32.copysign(-1.0), -2.3f32);
/*let f = 9.4f32;
println!("{}", f);*/
//println!("{}", 9.4f32); // FIXME: this is using bytes_in_context(), but gives a wrong value.

/*extern {
//pub fn printf(format: *const i8, ...) -> i32;
pub fn printf(format: *const i8, arg: f64) -> i32;
}

unsafe {
printf(b"Num: %f\n\0" as *const _ as *const _, 9.4f64);
}
println!("{}", 9.4f64);*/

let mut value = 0;
let res = unsafe { std::intrinsics::atomic_cxchg(&mut value, 0, 1) };
println!("{:?}", res);
let res = unsafe { std::intrinsics::atomic_cxchg(&mut value, 0, 1) };
println!("{:?}", res);

use std::sync::atomic::{AtomicBool, Ordering};

let a = AtomicBool::new(false);
assert_eq!(a.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst), Ok(false));
assert_eq!(a.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst), Err(true));

a.store(false, Ordering::SeqCst);
assert_eq!(a.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst), Ok(false));

// FIXME: the code seems to be the same when using an integer, but somehow, it doesn't work for
// a float. Could it be related to the fact that floating-points use different registers?

/*let f = 1234567.89f64;
assert_eq!("1.23456789e6", format!("{:e}", f));
println!("{:e}", f);
println!("{:e}", 1234567.89f64);*/
//assert_eq!("1.23456789e6", format!("{:e}", 1234567.89f64));
/*assert_eq!("1.23456789e3", format!("{:e}", 1234.56789f64));
assert_eq!("1.23456789E6", format!("{:E}", 1234567.89f64));
assert_eq!("1.23456789E3", format!("{:E}", 1234.56789f64));
assert_eq!("0.0", format!("{:?}", 0.0f64));
assert_eq!("1.01", format!("{:?}", 1.01f64));*/

/*assert_eq!((-0.0 as f64).min(-9.0), -9.0);
assert_eq!((f64::INFINITY as f64).min(9.0), 9.0);
assert_eq!((9.0 as f64).min(f64::INFINITY), 9.0);
assert_eq!((f64::INFINITY as f64).min(-9.0), -9.0);
assert_eq!((-9.0 as f64).min(f64::INFINITY), -9.0);
assert_eq!((f64::NEG_INFINITY as f64).min(9.0), f64::NEG_INFINITY);
assert_eq!((9.0 as f64).min(f64::NEG_INFINITY), f64::NEG_INFINITY);
assert_eq!((f64::NEG_INFINITY as f64).min(-9.0), f64::NEG_INFINITY);
assert_eq!((-9.0 as f64).min(f64::NEG_INFINITY), f64::NEG_INFINITY);*/
// Cranelift fmin has NaN propagation
//assert_eq!((f64::NAN as f64).min(9.0), 9.0);
//assert_eq!((f64::NAN as f64).min(-9.0), -9.0);
//assert_eq!((9.0 as f64).min(f64::NAN), 9.0);
//assert_eq!((-9.0 as f64).min(f64::NAN), -9.0);
//assert!((f64::NAN as f64).min(f64::NAN).is_nan());

/*let max: f64 = f32::MAX.into();
assert_eq!(max as f32, f32::MAX);
assert!(max.is_normal());

let min: f64 = f32::MIN.into();
assert_eq!(min as f32, f32::MIN);
assert!(min.is_normal());

let min_positive: f64 = f32::MIN_POSITIVE.into();
assert_eq!(min_positive as f32, f32::MIN_POSITIVE);
assert!(min_positive.is_normal());

let epsilon: f64 = f32::EPSILON.into();
assert_eq!(epsilon as f32, f32::EPSILON);
assert!(epsilon.is_normal());

let zero: f64 = (0.0f32).into();
assert_eq!(zero as f32, 0.0f32);
assert!(zero.is_sign_positive());

let neg_zero: f64 = (-0.0f32).into();
assert_eq!(neg_zero as f32, -0.0f32);
assert!(neg_zero.is_sign_negative());

let infinity: f64 = f32::INFINITY.into();
assert_eq!(infinity as f32, f32::INFINITY);
assert!(infinity.is_infinite());
assert!(infinity.is_sign_positive());

let neg_infinity: f64 = f32::NEG_INFINITY.into();
assert_eq!(neg_infinity as f32, f32::NEG_INFINITY);
assert!(neg_infinity.is_infinite());
assert!(neg_infinity.is_sign_negative());

let nan: f64 = f32::NAN.into();
assert!(nan.is_nan());*/

/*use std::convert::TryFrom;

/*let max = <i128>::MAX;
let min = <i128>::MIN;*/
Expand All @@ -35,4 +159,5 @@ fn main() {
<u64 as TryFrom<i128>>::try_from(t_min as i128).unwrap(),
t_min as u64
);*/
*/
}
17 changes: 10 additions & 7 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
};

let cond1 = self.context.new_comparison(None, comparison_operator, previous_var.to_rvalue(), self.context.new_cast(None, src, previous_value.get_type()));
let compare_exchange = self.compare_exchange(dst, previous_var.to_rvalue(), src, order, load_ordering, false);
let compare_exchange = self.compare_exchange(dst, previous_var, src, order, load_ordering, false);
let cond2 = self.cx.context.new_unary_op(None, UnaryOp::LogicalNegate, compare_exchange.get_type(), compare_exchange);
let cond = self.cx.context.new_binary_op(None, BinaryOp::LogicalAnd, self.cx.bool_type, cond1, cond2);

Expand All @@ -137,7 +137,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
return_value.to_rvalue()
}

fn compare_exchange(&self, dst: RValue<'gcc>, cmp: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
fn compare_exchange(&self, dst: RValue<'gcc>, cmp: LValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
let size = self.cx.int_width(src.get_type());
let compare_exchange = self.context.get_builtin_function(&format!("__atomic_compare_exchange_{}", size / 8));
let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
Expand All @@ -147,9 +147,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let void_ptr_type = self.context.new_type::<*mut ()>();
let volatile_void_ptr_type = void_ptr_type.make_volatile();
let dst = self.context.new_cast(None, dst, volatile_void_ptr_type);
let expected = self.current_func().new_local(None, cmp.get_type(), "expected");
self.llbb().add_assignment(None, expected, cmp);
let expected = self.context.new_cast(None, expected.get_address(None), void_ptr_type);
let expected = self.context.new_cast(None, cmp.get_address(None), void_ptr_type);

// NOTE: not sure why, but we need to cast to the signed type.
let new_src_type =
Expand Down Expand Up @@ -1494,17 +1492,22 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {

// Atomic Operations
fn atomic_cmpxchg(&mut self, dst: RValue<'gcc>, cmp: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
let success = self.compare_exchange(dst, cmp, src, order, failure_order, weak);
let expected = self.current_func().new_local(None, cmp.get_type(), "expected");
self.llbb().add_assignment(None, expected, cmp);
let success = self.compare_exchange(dst, expected, src, order, failure_order, weak);

let pair_type = self.cx.type_struct(&[src.get_type(), self.bool_type], false);
let result = self.current_func().new_local(None, pair_type, "atomic_cmpxchg_result");
let align = Align::from_bits(64).expect("align"); // TODO: use good align.

let value_type = result.to_rvalue().get_type();
if let Some(struct_type) = value_type.is_struct() {
self.store(cmp, result.access_field(None, struct_type.get_field(0)).get_address(None), align);
self.store(success, result.access_field(None, struct_type.get_field(1)).get_address(None), align);
// NOTE: since success contains the call to the intrinsic, it must be stored before
// expected so that we store expected after the call.
self.store(expected.to_rvalue(), result.access_field(None, struct_type.get_field(0)).get_address(None), align);
}
// TODO: handle when value is not a struct.

result.to_rvalue()
}
Expand Down