Skip to content

Commit 784ee5f

Browse files
authored
Rollup merge of rust-lang#144192 - RalfJung:atomicrmw-ptr, r=nikic
atomicrmw on pointers: move integer-pointer cast hacks into backend Conceptually, we want to have atomic operations on pointers of the form `fn atomic_add(ptr: *mut T, offset: usize, ...)`. However, LLVM does not directly support such operations (llvm/llvm-project#120837), so we have to cast the `offset` to a pointer somewhere. This PR moves that hack into the LLVM backend, so that the standard library, intrinsic, and Miri all work with the conceptual operation we actually want. Hopefully, one day LLVM will gain a way to represent these operations without integer-pointer casts, and then the hack will disappear entirely. Cc `@nikic` -- this is the best we can do right now, right? Fixes rust-lang#134617
2 parents 4f27c35 + de1b999 commit 784ee5f

File tree

18 files changed

+255
-181
lines changed

18 files changed

+255
-181
lines changed

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
969969

970970
let layout = amount.layout();
971971
match layout.ty.kind() {
972-
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
972+
ty::Uint(_) | ty::Int(_) => {}
973973
_ => {
974974
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
975975
return Ok(());
@@ -982,7 +982,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
982982
let old =
983983
fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount);
984984

985-
let old = CValue::by_val(old, layout);
985+
let old = CValue::by_val(old, ret.layout());
986986
ret.write_cvalue(fx, old);
987987
}
988988
sym::atomic_xsub => {
@@ -991,7 +991,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
991991

992992
let layout = amount.layout();
993993
match layout.ty.kind() {
994-
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
994+
ty::Uint(_) | ty::Int(_) => {}
995995
_ => {
996996
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
997997
return Ok(());
@@ -1004,7 +1004,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10041004
let old =
10051005
fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount);
10061006

1007-
let old = CValue::by_val(old, layout);
1007+
let old = CValue::by_val(old, ret.layout());
10081008
ret.write_cvalue(fx, old);
10091009
}
10101010
sym::atomic_and => {
@@ -1013,7 +1013,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10131013

10141014
let layout = src.layout();
10151015
match layout.ty.kind() {
1016-
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
1016+
ty::Uint(_) | ty::Int(_) => {}
10171017
_ => {
10181018
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
10191019
return Ok(());
@@ -1025,7 +1025,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10251025

10261026
let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::And, ptr, src);
10271027

1028-
let old = CValue::by_val(old, layout);
1028+
let old = CValue::by_val(old, ret.layout());
10291029
ret.write_cvalue(fx, old);
10301030
}
10311031
sym::atomic_or => {
@@ -1034,7 +1034,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10341034

10351035
let layout = src.layout();
10361036
match layout.ty.kind() {
1037-
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
1037+
ty::Uint(_) | ty::Int(_) => {}
10381038
_ => {
10391039
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
10401040
return Ok(());
@@ -1046,7 +1046,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10461046

10471047
let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Or, ptr, src);
10481048

1049-
let old = CValue::by_val(old, layout);
1049+
let old = CValue::by_val(old, ret.layout());
10501050
ret.write_cvalue(fx, old);
10511051
}
10521052
sym::atomic_xor => {
@@ -1055,7 +1055,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10551055

10561056
let layout = src.layout();
10571057
match layout.ty.kind() {
1058-
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
1058+
ty::Uint(_) | ty::Int(_) => {}
10591059
_ => {
10601060
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
10611061
return Ok(());
@@ -1067,7 +1067,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10671067

10681068
let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Xor, ptr, src);
10691069

1070-
let old = CValue::by_val(old, layout);
1070+
let old = CValue::by_val(old, ret.layout());
10711071
ret.write_cvalue(fx, old);
10721072
}
10731073
sym::atomic_nand => {
@@ -1076,7 +1076,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10761076

10771077
let layout = src.layout();
10781078
match layout.ty.kind() {
1079-
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
1079+
ty::Uint(_) | ty::Int(_) => {}
10801080
_ => {
10811081
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
10821082
return Ok(());
@@ -1088,7 +1088,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
10881088

10891089
let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Nand, ptr, src);
10901090

1091-
let old = CValue::by_val(old, layout);
1091+
let old = CValue::by_val(old, ret.layout());
10921092
ret.write_cvalue(fx, old);
10931093
}
10941094
sym::atomic_max => {

compiler/rustc_codegen_gcc/src/builder.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1671,6 +1671,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
16711671
dst: RValue<'gcc>,
16721672
src: RValue<'gcc>,
16731673
order: AtomicOrdering,
1674+
ret_ptr: bool,
16741675
) -> RValue<'gcc> {
16751676
let size = get_maybe_pointer_size(src);
16761677
let name = match op {
@@ -1698,14 +1699,18 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
16981699
let atomic_function = self.context.get_builtin_function(name);
16991700
let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
17001701

1702+
// FIXME: If `ret_ptr` is true and `src` is an integer, we should really tell GCC
1703+
// that this is a pointer operation that needs to preserve provenance -- but like LLVM,
1704+
// GCC does not currently seems to support that.
17011705
let void_ptr_type = self.context.new_type::<*mut ()>();
17021706
let volatile_void_ptr_type = void_ptr_type.make_volatile();
17031707
let dst = self.context.new_cast(self.location, dst, volatile_void_ptr_type);
17041708
// FIXME(antoyo): not sure why, but we have the wrong type here.
17051709
let new_src_type = atomic_function.get_param(1).to_rvalue().get_type();
17061710
let src = self.context.new_bitcast(self.location, src, new_src_type);
17071711
let res = self.context.new_call(self.location, atomic_function, &[dst, src, order]);
1708-
self.context.new_cast(self.location, res, src.get_type())
1712+
let res_type = if ret_ptr { void_ptr_type } else { src.get_type() };
1713+
self.context.new_cast(self.location, res, res_type)
17091714
}
17101715

17111716
fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope) {

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,15 +1327,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13271327
&mut self,
13281328
op: rustc_codegen_ssa::common::AtomicRmwBinOp,
13291329
dst: &'ll Value,
1330-
mut src: &'ll Value,
1330+
src: &'ll Value,
13311331
order: rustc_middle::ty::AtomicOrdering,
1332+
ret_ptr: bool,
13321333
) -> &'ll Value {
1333-
// The only RMW operation that LLVM supports on pointers is compare-exchange.
1334-
let requires_cast_to_int = self.val_ty(src) == self.type_ptr()
1335-
&& op != rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicXchg;
1336-
if requires_cast_to_int {
1337-
src = self.ptrtoint(src, self.type_isize());
1338-
}
1334+
// FIXME: If `ret_ptr` is true and `src` is not a pointer, we *should* tell LLVM that the
1335+
// LHS is a pointer and the operation should be provenance-preserving, but LLVM does not
1336+
// currently support that (https://github.com/llvm/llvm-project/issues/120837).
13391337
let mut res = unsafe {
13401338
llvm::LLVMBuildAtomicRMW(
13411339
self.llbuilder,
@@ -1346,7 +1344,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13461344
llvm::False, // SingleThreaded
13471345
)
13481346
};
1349-
if requires_cast_to_int {
1347+
if ret_ptr && self.val_ty(res) != self.type_ptr() {
13501348
res = self.inttoptr(res, self.type_ptr());
13511349
}
13521350
res

compiler/rustc_codegen_ssa/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization
101101
102102
codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}`
103103
104+
codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer or pointer type, found `{$ty}`
105+
104106
codegen_ssa_invalid_monomorphization_cannot_return = invalid monomorphization of `{$name}` intrinsic: cannot return `{$ret_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
105107
106108
codegen_ssa_invalid_monomorphization_cast_wide_pointer = invalid monomorphization of `{$name}` intrinsic: cannot cast wide pointer `{$ty}`

compiler/rustc_codegen_ssa/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,14 @@ pub enum InvalidMonomorphization<'tcx> {
764764
ty: Ty<'tcx>,
765765
},
766766

767+
#[diag(codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type, code = E0511)]
768+
BasicIntegerOrPtrType {
769+
#[primary_span]
770+
span: Span,
771+
name: Symbol,
772+
ty: Ty<'tcx>,
773+
},
774+
767775
#[diag(codegen_ssa_invalid_monomorphization_basic_float_type, code = E0511)]
768776
BasicFloatType {
769777
#[primary_span]

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
9292
let invalid_monomorphization_int_type = |ty| {
9393
bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
9494
};
95+
let invalid_monomorphization_int_or_ptr_type = |ty| {
96+
bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerOrPtrType {
97+
span,
98+
name,
99+
ty,
100+
});
101+
};
95102

96103
let parse_atomic_ordering = |ord: ty::Value<'tcx>| {
97104
let discr = ord.valtree.unwrap_branch()[0].unwrap_leaf();
@@ -351,7 +358,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
351358
sym::atomic_load => {
352359
let ty = fn_args.type_at(0);
353360
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
354-
invalid_monomorphization_int_type(ty);
361+
invalid_monomorphization_int_or_ptr_type(ty);
355362
return Ok(());
356363
}
357364
let ordering = fn_args.const_at(1).to_value();
@@ -367,7 +374,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
367374
sym::atomic_store => {
368375
let ty = fn_args.type_at(0);
369376
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
370-
invalid_monomorphization_int_type(ty);
377+
invalid_monomorphization_int_or_ptr_type(ty);
371378
return Ok(());
372379
}
373380
let ordering = fn_args.const_at(1).to_value();
@@ -377,10 +384,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
377384
bx.atomic_store(val, ptr, parse_atomic_ordering(ordering), size);
378385
return Ok(());
379386
}
387+
// These are all AtomicRMW ops
380388
sym::atomic_cxchg | sym::atomic_cxchgweak => {
381389
let ty = fn_args.type_at(0);
382390
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
383-
invalid_monomorphization_int_type(ty);
391+
invalid_monomorphization_int_or_ptr_type(ty);
384392
return Ok(());
385393
}
386394
let succ_ordering = fn_args.const_at(1).to_value();
@@ -407,7 +415,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
407415

408416
return Ok(());
409417
}
410-
// These are all AtomicRMW ops
411418
sym::atomic_max | sym::atomic_min => {
412419
let atom_op = if name == sym::atomic_max {
413420
AtomicRmwBinOp::AtomicMax
@@ -420,7 +427,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
420427
let ordering = fn_args.const_at(1).to_value();
421428
let ptr = args[0].immediate();
422429
let val = args[1].immediate();
423-
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
430+
bx.atomic_rmw(
431+
atom_op,
432+
ptr,
433+
val,
434+
parse_atomic_ordering(ordering),
435+
/* ret_ptr */ false,
436+
)
424437
} else {
425438
invalid_monomorphization_int_type(ty);
426439
return Ok(());
@@ -438,21 +451,44 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
438451
let ordering = fn_args.const_at(1).to_value();
439452
let ptr = args[0].immediate();
440453
let val = args[1].immediate();
441-
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
454+
bx.atomic_rmw(
455+
atom_op,
456+
ptr,
457+
val,
458+
parse_atomic_ordering(ordering),
459+
/* ret_ptr */ false,
460+
)
442461
} else {
443462
invalid_monomorphization_int_type(ty);
444463
return Ok(());
445464
}
446465
}
447-
sym::atomic_xchg
448-
| sym::atomic_xadd
466+
sym::atomic_xchg => {
467+
let ty = fn_args.type_at(0);
468+
let ordering = fn_args.const_at(1).to_value();
469+
if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
470+
let ptr = args[0].immediate();
471+
let val = args[1].immediate();
472+
let atomic_op = AtomicRmwBinOp::AtomicXchg;
473+
bx.atomic_rmw(
474+
atomic_op,
475+
ptr,
476+
val,
477+
parse_atomic_ordering(ordering),
478+
/* ret_ptr */ ty.is_raw_ptr(),
479+
)
480+
} else {
481+
invalid_monomorphization_int_or_ptr_type(ty);
482+
return Ok(());
483+
}
484+
}
485+
sym::atomic_xadd
449486
| sym::atomic_xsub
450487
| sym::atomic_and
451488
| sym::atomic_nand
452489
| sym::atomic_or
453490
| sym::atomic_xor => {
454491
let atom_op = match name {
455-
sym::atomic_xchg => AtomicRmwBinOp::AtomicXchg,
456492
sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
457493
sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
458494
sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
@@ -462,14 +498,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
462498
_ => unreachable!(),
463499
};
464500

465-
let ty = fn_args.type_at(0);
466-
if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
467-
let ordering = fn_args.const_at(1).to_value();
468-
let ptr = args[0].immediate();
469-
let val = args[1].immediate();
470-
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
501+
// The type of the in-memory data.
502+
let ty_mem = fn_args.type_at(0);
503+
// The type of the 2nd operand, given by-value.
504+
let ty_op = fn_args.type_at(1);
505+
506+
let ordering = fn_args.const_at(2).to_value();
507+
// We require either both arguments to have the same integer type, or the first to
508+
// be a pointer and the second to be `usize`.
509+
if (int_type_width_signed(ty_mem, bx.tcx()).is_some() && ty_op == ty_mem)
510+
|| (ty_mem.is_raw_ptr() && ty_op == bx.tcx().types.usize)
511+
{
512+
let ptr = args[0].immediate(); // of type "pointer to `ty_mem`"
513+
let val = args[1].immediate(); // of type `ty_op`
514+
bx.atomic_rmw(
515+
atom_op,
516+
ptr,
517+
val,
518+
parse_atomic_ordering(ordering),
519+
/* ret_ptr */ ty_mem.is_raw_ptr(),
520+
)
471521
} else {
472-
invalid_monomorphization_int_type(ty);
522+
invalid_monomorphization_int_or_ptr_type(ty_mem);
473523
return Ok(());
474524
}
475525
}

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,12 +548,15 @@ pub trait BuilderMethods<'a, 'tcx>:
548548
failure_order: AtomicOrdering,
549549
weak: bool,
550550
) -> (Self::Value, Self::Value);
551+
/// `ret_ptr` indicates whether the return type (which is also the type `dst` points to)
552+
/// is a pointer or the same type as `src`.
551553
fn atomic_rmw(
552554
&mut self,
553555
op: AtomicRmwBinOp,
554556
dst: Self::Value,
555557
src: Self::Value,
556558
order: AtomicOrdering,
559+
ret_ptr: bool,
557560
) -> Self::Value;
558561
fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope);
559562
fn set_invariant_load(&mut self, load: Self::Value);

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -652,16 +652,16 @@ pub(crate) fn check_intrinsic_type(
652652
sym::atomic_store => (1, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit),
653653

654654
sym::atomic_xchg
655-
| sym::atomic_xadd
656-
| sym::atomic_xsub
657-
| sym::atomic_and
658-
| sym::atomic_nand
659-
| sym::atomic_or
660-
| sym::atomic_xor
661655
| sym::atomic_max
662656
| sym::atomic_min
663657
| sym::atomic_umax
664658
| sym::atomic_umin => (1, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)),
659+
sym::atomic_xadd
660+
| sym::atomic_xsub
661+
| sym::atomic_and
662+
| sym::atomic_nand
663+
| sym::atomic_or
664+
| sym::atomic_xor => (2, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(1)], param(0)),
665665
sym::atomic_fence | sym::atomic_singlethreadfence => (0, 1, Vec::new(), tcx.types.unit),
666666

667667
other => {

0 commit comments

Comments
 (0)