Skip to content

Commit fbb2ca5

Browse files
authored
Rollup merge of rust-lang#134070 - oli-obk:push-nquzymupzlsq, r=jieyouxu
Some asm! diagnostic adjustments and a papercut fix Best reviewed commit by commit. We forgot a `normalize` call in intrinsic checking, causing us to allow literal integers, but not named constants containing that literal. This can in theory affect stable code, but only if libstd contains a stable SIMD type that has an array length that is a named constant. I'd assume we'd have noticed by now due to asm! rejecting those outright. The error message left me scratching my head for a bit, so I added some extra information to the diagnostic, too.
2 parents 61d55dd + c7088b2 commit fbb2ca5

File tree

4 files changed

+130
-44
lines changed

4 files changed

+130
-44
lines changed

compiler/rustc_hir_analysis/src/check/intrinsicck.rs

+74-44
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::assert_matches::debug_assert_matches;
33
use rustc_abi::FieldIdx;
44
use rustc_ast::InlineAsmTemplatePiece;
55
use rustc_data_structures::fx::FxIndexSet;
6+
use rustc_hir::def_id::DefId;
67
use rustc_hir::{self as hir, LangItem};
78
use rustc_middle::bug;
89
use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
@@ -21,6 +22,12 @@ pub struct InlineAsmCtxt<'a, 'tcx> {
2122
get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
2223
}
2324

25+
enum NonAsmTypeReason<'tcx> {
26+
UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>),
27+
Invalid(Ty<'tcx>),
28+
InvalidElement(DefId, Ty<'tcx>),
29+
}
30+
2431
impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
2532
pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self {
2633
InlineAsmCtxt {
@@ -56,7 +63,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
5663
false
5764
}
5865

59-
fn get_asm_ty(&self, ty: Ty<'tcx>) -> Option<InlineAsmType> {
66+
fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> {
6067
let asm_ty_isize = match self.tcx.sess.target.pointer_width {
6168
16 => InlineAsmType::I16,
6269
32 => InlineAsmType::I32,
@@ -65,64 +72,62 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
6572
};
6673

6774
match *ty.kind() {
68-
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
69-
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
70-
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
71-
ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
72-
ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
73-
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
74-
ty::Float(FloatTy::F16) => Some(InlineAsmType::F16),
75-
ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
76-
ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
77-
ty::Float(FloatTy::F128) => Some(InlineAsmType::F128),
78-
ty::FnPtr(..) => Some(asm_ty_isize),
79-
ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize),
75+
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8),
76+
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16),
77+
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32),
78+
ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64),
79+
ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128),
80+
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize),
81+
ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16),
82+
ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32),
83+
ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64),
84+
ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128),
85+
ty::FnPtr(..) => Ok(asm_ty_isize),
86+
ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Ok(asm_ty_isize),
8087
ty::Adt(adt, args) if adt.repr().simd() => {
8188
let fields = &adt.non_enum_variant().fields;
82-
let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
89+
let field = &fields[FieldIdx::ZERO];
90+
let elem_ty = field.ty(self.tcx, args);
8391

8492
let (size, ty) = match elem_ty.kind() {
8593
ty::Array(ty, len) => {
94+
let len = self.tcx.normalize_erasing_regions(self.typing_env, *len);
8695
if let Some(len) = len.try_to_target_usize(self.tcx) {
8796
(len, *ty)
8897
} else {
89-
return None;
98+
return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength(
99+
field.did, len,
100+
));
90101
}
91102
}
92103
_ => (fields.len() as u64, elem_ty),
93104
};
94105

95106
match ty.kind() {
96-
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::VecI8(size)),
97-
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
98-
Some(InlineAsmType::VecI16(size))
99-
}
100-
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => {
101-
Some(InlineAsmType::VecI32(size))
102-
}
103-
ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => {
104-
Some(InlineAsmType::VecI64(size))
105-
}
107+
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)),
108+
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)),
109+
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)),
110+
ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)),
106111
ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
107-
Some(InlineAsmType::VecI128(size))
112+
Ok(InlineAsmType::VecI128(size))
108113
}
109114
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
110-
Some(match self.tcx.sess.target.pointer_width {
115+
Ok(match self.tcx.sess.target.pointer_width {
111116
16 => InlineAsmType::VecI16(size),
112117
32 => InlineAsmType::VecI32(size),
113118
64 => InlineAsmType::VecI64(size),
114119
width => bug!("unsupported pointer width: {width}"),
115120
})
116121
}
117-
ty::Float(FloatTy::F16) => Some(InlineAsmType::VecF16(size)),
118-
ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)),
119-
ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(size)),
120-
ty::Float(FloatTy::F128) => Some(InlineAsmType::VecF128(size)),
121-
_ => None,
122+
ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)),
123+
ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)),
124+
ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)),
125+
ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)),
126+
_ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)),
122127
}
123128
}
124129
ty::Infer(_) => bug!("unexpected infer ty in asm operand"),
125-
_ => None,
130+
_ => Err(NonAsmTypeReason::Invalid(ty)),
126131
}
127132
}
128133

@@ -163,17 +168,42 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
163168
}
164169
_ => self.get_asm_ty(ty),
165170
};
166-
let Some(asm_ty) = asm_ty else {
167-
let msg = format!("cannot use value of type `{ty}` for inline assembly");
168-
self.tcx
169-
.dcx()
170-
.struct_span_err(expr.span, msg)
171-
.with_note(
172-
"only integers, floats, SIMD vectors, pointers and function pointers \
173-
can be used as arguments for inline assembly",
174-
)
175-
.emit();
176-
return None;
171+
let asm_ty = match asm_ty {
172+
Ok(asm_ty) => asm_ty,
173+
Err(reason) => {
174+
match reason {
175+
NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => {
176+
let msg = format!("cannot evaluate SIMD vector length `{len}`");
177+
self.tcx
178+
.dcx()
179+
.struct_span_err(self.tcx.def_span(did), msg)
180+
.with_span_note(
181+
expr.span,
182+
"SIMD vector length needs to be known statically for use in `asm!`",
183+
)
184+
.emit();
185+
}
186+
NonAsmTypeReason::Invalid(ty) => {
187+
let msg = format!("cannot use value of type `{ty}` for inline assembly");
188+
self.tcx.dcx().struct_span_err(expr.span, msg).with_note(
189+
"only integers, floats, SIMD vectors, pointers and function pointers \
190+
can be used as arguments for inline assembly",
191+
).emit();
192+
}
193+
NonAsmTypeReason::InvalidElement(did, ty) => {
194+
let msg = format!(
195+
"cannot use SIMD vector with element type `{ty}` for inline assembly"
196+
);
197+
self.tcx.dcx()
198+
.struct_span_err(self.tcx.def_span(did), msg).with_span_note(
199+
expr.span,
200+
"only integers, floats, SIMD vectors, pointers and function pointers \
201+
can be used as arguments for inline assembly",
202+
).emit();
203+
}
204+
}
205+
return None;
206+
}
177207
};
178208

179209
// Check that the type implements Copy. The only case where this can
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//! This is a regression test to ensure that we emit a diagnostic pointing to the
2+
//! reason the type was rejected in inline assembly.
3+
4+
//@ only-x86_64
5+
6+
#![feature(repr_simd)]
7+
8+
#[repr(simd)]
9+
#[derive(Copy, Clone)]
10+
pub struct Foo<const C: usize>([u8; C]);
11+
//~^ ERROR: cannot evaluate SIMD vector length
12+
13+
pub unsafe fn foo<const C: usize>(a: Foo<C>) {
14+
std::arch::asm!(
15+
"movaps {src}, {src}",
16+
src = in(xmm_reg) a,
17+
//~^ NOTE: SIMD vector length needs to be known statically
18+
);
19+
}
20+
21+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: cannot evaluate SIMD vector length `C`
2+
--> $DIR/generic_const_simd_vec_len.rs:10:32
3+
|
4+
LL | pub struct Foo<const C: usize>([u8; C]);
5+
| ^^^^^^^
6+
|
7+
note: SIMD vector length needs to be known statically for use in `asm!`
8+
--> $DIR/generic_const_simd_vec_len.rs:16:27
9+
|
10+
LL | src = in(xmm_reg) a,
11+
| ^
12+
13+
error: aborting due to 1 previous error
14+
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//! This is a regression test to ensure that we evaluate
2+
//! SIMD vector length constants instead of assuming they are literals.
3+
4+
//@check-pass
5+
6+
#![feature(repr_simd)]
7+
8+
const C: usize = 16;
9+
10+
#[repr(simd)]
11+
#[derive(Copy, Clone)]
12+
pub struct Foo([u8; C]);
13+
14+
pub unsafe fn foo(a: Foo) {
15+
std::arch::asm!(
16+
"movaps {src}, {src}",
17+
src = in(xmm_reg) a,
18+
);
19+
}
20+
21+
fn main() {}

0 commit comments

Comments
 (0)