Skip to content

Commit 97fff1f

Browse files
committed
Auto merge of rust-lang#114790 - taiki-e:asm-maybe-uninit, r=Amanieu
Allow MaybeUninit in input and output of inline assembly **Motivation:** As part of the work to remove UBs from crossbeam's AtomicCell, I'm writing a library to implement atomic operations on MaybeUnint using inline assembly ([atomic-maybe-uninit](https://github.com/taiki-e/atomic-maybe-uninit), crossbeam-rs/crossbeam#1015). However, currently, MaybeUnint cannot be used in input&output of inline assembly, so when processing MaybeUninit, values must be [passed through memory](https://github.com/taiki-e/atomic-maybe-uninit/blob/main/src/arch/aarch64.rs#L121-L122). It is inefficient and microbenchmarks have [actually shown significant performance degradation](crossbeam-rs/crossbeam#1015 (comment)). It would be nice if we could allow MaybeUninit in input and output of inline assembly. --- This PR changed the type check in rustc_hir_analysis to allow `MaybeUnint<int | float | ptr | fn ptr | simd vector>` in input and output of inline assembly and added a simple test. To be honest, I'm not sure that this is the correct way to do it, because this is like doing transmute to integers/floats/etc from MaybeUninit on the compiler side. EDIT: [this seems fine](https://rust-lang.zulipchat.com/#narrow/stream/216763-project-inline-asm/topic/MaybeUninit.20in.20asm!/near/384662900) r? `@Amanieu` cc `@thomcc` (because you [had previously proposed this](https://rust-lang.zulipchat.com/#narrow/stream/216763-project-inline-asm/topic/MaybeUninit.20in.20asm!))
2 parents 544dea6 + 03fd2d4 commit 97fff1f

File tree

2 files changed

+61
-19
lines changed

2 files changed

+61
-19
lines changed

compiler/rustc_hir_analysis/src/check/intrinsicck.rs

+34-19
Original file line numberDiff line numberDiff line change
@@ -44,31 +44,15 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
4444
false
4545
}
4646

47-
fn check_asm_operand_type(
48-
&self,
49-
idx: usize,
50-
reg: InlineAsmRegOrRegClass,
51-
expr: &'tcx hir::Expr<'tcx>,
52-
template: &[InlineAsmTemplatePiece],
53-
is_input: bool,
54-
tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
55-
target_features: &FxIndexSet<Symbol>,
56-
) -> Option<InlineAsmType> {
57-
let ty = (self.get_operand_ty)(expr);
58-
if ty.has_non_region_infer() {
59-
bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
60-
}
47+
fn get_asm_ty(&self, ty: Ty<'tcx>) -> Option<InlineAsmType> {
6148
let asm_ty_isize = match self.tcx.sess.target.pointer_width {
6249
16 => InlineAsmType::I16,
6350
32 => InlineAsmType::I32,
6451
64 => InlineAsmType::I64,
6552
_ => unreachable!(),
6653
};
6754

68-
let asm_ty = match *ty.kind() {
69-
// `!` is allowed for input but not for output (issue #87802)
70-
ty::Never if is_input => return None,
71-
_ if ty.references_error() => return None,
55+
match *ty.kind() {
7256
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
7357
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
7458
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
@@ -99,7 +83,6 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
9983
};
10084

10185
match ty.kind() {
102-
ty::Never | ty::Error(_) => return None,
10386
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::VecI8(size)),
10487
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
10588
Some(InlineAsmType::VecI16(size))
@@ -128,6 +111,38 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
128111
}
129112
ty::Infer(_) => unreachable!(),
130113
_ => None,
114+
}
115+
}
116+
117+
fn check_asm_operand_type(
118+
&self,
119+
idx: usize,
120+
reg: InlineAsmRegOrRegClass,
121+
expr: &'tcx hir::Expr<'tcx>,
122+
template: &[InlineAsmTemplatePiece],
123+
is_input: bool,
124+
tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
125+
target_features: &FxIndexSet<Symbol>,
126+
) -> Option<InlineAsmType> {
127+
let ty = (self.get_operand_ty)(expr);
128+
if ty.has_non_region_infer() {
129+
bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
130+
}
131+
132+
let asm_ty = match *ty.kind() {
133+
// `!` is allowed for input but not for output (issue #87802)
134+
ty::Never if is_input => return None,
135+
_ if ty.references_error() => return None,
136+
ty::Adt(adt, args) if Some(adt.did()) == self.tcx.lang_items().maybe_uninit() => {
137+
let fields = &adt.non_enum_variant().fields;
138+
let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args);
139+
let ty::Adt(ty, args) = ty.kind() else { unreachable!() };
140+
assert!(ty.is_manually_drop());
141+
let fields = &ty.non_enum_variant().fields;
142+
let ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args);
143+
self.get_asm_ty(ty)
144+
}
145+
_ => self.get_asm_ty(ty),
131146
};
132147
let Some(asm_ty) = asm_ty else {
133148
let msg = format!("cannot use value of type `{ty}` for inline assembly");

tests/codegen/asm-maybe-uninit.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// compile-flags: -O
2+
// only-x86_64
3+
4+
#![crate_type = "rlib"]
5+
#![allow(asm_sub_register)]
6+
7+
use std::mem::MaybeUninit;
8+
use std::arch::asm;
9+
10+
// CHECK-LABEL: @int
11+
#[no_mangle]
12+
pub unsafe fn int(x: MaybeUninit<i32>) -> MaybeUninit<i32> {
13+
let y: MaybeUninit<i32>;
14+
asm!("/*{}{}*/", in(reg) x, out(reg) y);
15+
y
16+
}
17+
18+
// CHECK-LABEL: @inout
19+
#[no_mangle]
20+
pub unsafe fn inout(mut x: i32) -> MaybeUninit<u32> {
21+
let mut y: MaybeUninit<u32>;
22+
asm!("/*{}*/", inout(reg) x => y);
23+
asm!("/*{}*/", inout(reg) y => x);
24+
asm!("/*{}*/", inlateout(reg) x => y);
25+
asm!("/*{}*/", inlateout(reg) y => x);
26+
y
27+
}

0 commit comments

Comments
 (0)