Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit f5f0c48

Browse files
committed
Auto merge of rust-lang#15194 - HKalbasi:mir, r=HKalbasi
Fix layout of simd types and respect align in mir interpreter
2 parents 3d3f325 + 6f74528 commit f5f0c48

File tree

6 files changed

+189
-22
lines changed

6 files changed

+189
-22
lines changed

crates/hir-def/src/data/adt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
147147
}
148148
"C" => ReprFlags::IS_C,
149149
"transparent" => ReprFlags::IS_TRANSPARENT,
150+
"simd" => ReprFlags::IS_SIMD,
150151
repr => {
151152
if let Some(builtin) = BuiltinInt::from_suffix(repr)
152153
.map(Either::Left)

crates/hir-ty/src/consteval/tests.rs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
3636

3737
#[track_caller]
3838
fn check_number(ra_fixture: &str, answer: i128) {
39+
check_answer(ra_fixture, |b| {
40+
assert_eq!(
41+
b,
42+
&answer.to_le_bytes()[0..b.len()],
43+
"Bytes differ. In decimal form: actual = {}, expected = {answer}",
44+
i128::from_le_bytes(pad16(b, true))
45+
);
46+
});
47+
}
48+
49+
#[track_caller]
50+
fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8])) {
3951
let (db, file_id) = TestDB::with_single_file(ra_fixture);
4052
let r = match eval_goal(&db, file_id) {
4153
Ok(t) => t,
@@ -47,12 +59,7 @@ fn check_number(ra_fixture: &str, answer: i128) {
4759
match &r.data(Interner).value {
4860
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
4961
ConstScalar::Bytes(b, _) => {
50-
assert_eq!(
51-
b,
52-
&answer.to_le_bytes()[0..b.len()],
53-
"Bytes differ. In decimal form: actual = {}, expected = {answer}",
54-
i128::from_le_bytes(pad16(b, true))
55-
);
62+
check(b);
5663
}
5764
x => panic!("Expected number but found {:?}", x),
5865
},
@@ -87,7 +94,7 @@ fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
8794
}
8895
_ => None,
8996
})
90-
.unwrap();
97+
.expect("No const named GOAL found in the test");
9198
db.const_eval(const_id.into(), Substitution::empty(Interner))
9299
}
93100

@@ -206,6 +213,30 @@ fn raw_pointer_equality() {
206213
);
207214
}
208215

216+
#[test]
217+
fn alignment() {
218+
check_answer(
219+
r#"
220+
//- minicore: transmute
221+
use core::mem::transmute;
222+
const GOAL: usize = {
223+
let x: i64 = 2;
224+
transmute(&x)
225+
}
226+
"#,
227+
|b| assert_eq!(b[0] % 8, 0),
228+
);
229+
check_answer(
230+
r#"
231+
//- minicore: transmute
232+
use core::mem::transmute;
233+
static X: i64 = 12;
234+
const GOAL: usize = transmute(&X);
235+
"#,
236+
|b| assert_eq!(b[0] % 8, 0),
237+
);
238+
}
239+
209240
#[test]
210241
fn locals() {
211242
check_number(

crates/hir-ty/src/layout.rs

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use hir_def::{
77
Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size,
88
StructKind, TargetDataLayout, WrappingRange,
99
},
10-
LocalEnumVariantId, LocalFieldId,
10+
LocalEnumVariantId, LocalFieldId, StructId,
1111
};
1212
use la_arena::{Idx, RawIdx};
1313
use stdx::never;
@@ -77,6 +77,78 @@ impl<'a> LayoutCalculator for LayoutCx<'a> {
7777
}
7878
}
7979

80+
// FIXME: move this to the `rustc_abi`.
81+
fn layout_of_simd_ty(
82+
db: &dyn HirDatabase,
83+
id: StructId,
84+
subst: &Substitution,
85+
krate: CrateId,
86+
dl: &TargetDataLayout,
87+
) -> Result<Arc<Layout>, LayoutError> {
88+
let fields = db.field_types(id.into());
89+
90+
// Supported SIMD vectors are homogeneous ADTs with at least one field:
91+
//
92+
// * #[repr(simd)] struct S(T, T, T, T);
93+
// * #[repr(simd)] struct S { x: T, y: T, z: T, w: T }
94+
// * #[repr(simd)] struct S([T; 4])
95+
//
96+
// where T is a primitive scalar (integer/float/pointer).
97+
98+
let f0_ty = match fields.iter().next() {
99+
Some(x) => x.1.clone().substitute(Interner, subst),
100+
None => {
101+
user_error!("simd type with zero fields");
102+
}
103+
};
104+
105+
// The element type and number of elements of the SIMD vector
106+
// are obtained from:
107+
//
108+
// * the element type and length of the single array field, if
109+
// the first field is of array type, or
110+
//
111+
// * the homogeneous field type and the number of fields.
112+
let (e_ty, e_len, is_array) = if let TyKind::Array(e_ty, _) = f0_ty.kind(Interner) {
113+
// Extract the number of elements from the layout of the array field:
114+
let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), krate)?.fields else {
115+
user_error!("Array with non array layout");
116+
};
117+
118+
(e_ty.clone(), count, true)
119+
} else {
120+
// First ADT field is not an array:
121+
(f0_ty, fields.iter().count() as u64, false)
122+
};
123+
124+
// Compute the ABI of the element type:
125+
let e_ly = db.layout_of_ty(e_ty, krate)?;
126+
let Abi::Scalar(e_abi) = e_ly.abi else {
127+
user_error!("simd type with inner non scalar type");
128+
};
129+
130+
// Compute the size and alignment of the vector:
131+
let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow)?;
132+
let align = dl.vector_align(size);
133+
let size = size.align_to(align.abi);
134+
135+
// Compute the placement of the vector fields:
136+
let fields = if is_array {
137+
FieldsShape::Arbitrary { offsets: [Size::ZERO].into(), memory_index: [0].into() }
138+
} else {
139+
FieldsShape::Array { stride: e_ly.size, count: e_len }
140+
};
141+
142+
Ok(Arc::new(Layout {
143+
variants: Variants::Single { index: struct_variant_idx() },
144+
fields,
145+
abi: Abi::Vector { element: e_abi, count: e_len },
146+
largest_niche: e_ly.largest_niche,
147+
size,
148+
align,
149+
}))
150+
}
151+
80152
pub fn layout_of_ty_query(
81153
db: &dyn HirDatabase,
82154
ty: Ty,
@@ -88,7 +160,16 @@ pub fn layout_of_ty_query(
88160
let trait_env = Arc::new(TraitEnvironment::empty(krate));
89161
let ty = normalize(db, trait_env, ty.clone());
90162
let result = match ty.kind(Interner) {
91-
TyKind::Adt(AdtId(def), subst) => return db.layout_of_adt(*def, subst.clone(), krate),
163+
TyKind::Adt(AdtId(def), subst) => {
164+
if let hir_def::AdtId::StructId(s) = def {
165+
let data = db.struct_data(*s);
166+
let repr = data.repr.unwrap_or_default();
167+
if repr.simd() {
168+
return layout_of_simd_ty(db, *s, subst, krate, &target);
169+
}
170+
};
171+
return db.layout_of_adt(*def, subst.clone(), krate);
172+
}
92173
TyKind::Scalar(s) => match s {
93174
chalk_ir::Scalar::Bool => Layout::scalar(
94175
dl,

crates/hir-ty/src/layout/tests.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,20 @@ struct Goal(Foo<S>);
270270
);
271271
}
272272

273+
#[test]
274+
fn simd_types() {
275+
check_size_and_align(
276+
r#"
277+
#[repr(simd)]
278+
struct SimdType(i64, i64);
279+
struct Goal(SimdType);
280+
"#,
281+
"",
282+
16,
283+
16,
284+
);
285+
}
286+
273287
#[test]
274288
fn return_position_impl_trait() {
275289
size_and_align_expr! {

crates/hir-ty/src/lib.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,16 @@ impl MemoryMap {
180180
/// allocator function as `f` and it will return a mapping of old addresses to new addresses.
181181
fn transform_addresses(
182182
&self,
183-
mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
183+
mut f: impl FnMut(&[u8], usize) -> Result<usize, MirEvalError>,
184184
) -> Result<HashMap<usize, usize>, MirEvalError> {
185-
self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
185+
self.memory
186+
.iter()
187+
.map(|x| {
188+
let addr = *x.0;
189+
let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) };
190+
Ok((addr, f(x.1, align)?))
191+
})
192+
.collect()
186193
}
187194

188195
fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> {

crates/hir-ty/src/mir/eval.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,26 @@ impl IntervalOrOwned {
226226
}
227227
}
228228

229+
#[cfg(target_pointer_width = "64")]
230+
const STACK_OFFSET: usize = 1 << 60;
231+
#[cfg(target_pointer_width = "64")]
232+
const HEAP_OFFSET: usize = 1 << 59;
233+
234+
#[cfg(target_pointer_width = "32")]
235+
const STACK_OFFSET: usize = 1 << 30;
236+
#[cfg(target_pointer_width = "32")]
237+
const HEAP_OFFSET: usize = 1 << 29;
238+
229239
impl Address {
230240
fn from_bytes(x: &[u8]) -> Result<Self> {
231241
Ok(Address::from_usize(from_bytes!(usize, x)))
232242
}
233243

234244
fn from_usize(x: usize) -> Self {
235-
if x > usize::MAX / 2 {
236-
Stack(x - usize::MAX / 2)
237-
} else if x > usize::MAX / 4 {
238-
Heap(x - usize::MAX / 4)
245+
if x > STACK_OFFSET {
246+
Stack(x - STACK_OFFSET)
247+
} else if x > HEAP_OFFSET {
248+
Heap(x - HEAP_OFFSET)
239249
} else {
240250
Invalid(x)
241251
}
@@ -247,8 +257,8 @@ impl Address {
247257

248258
fn to_usize(&self) -> usize {
249259
let as_num = match self {
250-
Stack(x) => *x + usize::MAX / 2,
251-
Heap(x) => *x + usize::MAX / 4,
260+
Stack(x) => *x + STACK_OFFSET,
261+
Heap(x) => *x + HEAP_OFFSET,
252262
Invalid(x) => *x,
253263
};
254264
as_num
@@ -721,8 +731,14 @@ impl Evaluator<'_> {
721731
.locals
722732
.iter()
723733
.map(|(id, x)| {
724-
let size =
725-
self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?;
734+
let (size, align) = self.size_align_of_sized(
735+
&x.ty,
736+
&locals,
737+
"no unsized local in extending stack",
738+
)?;
739+
while stack_ptr % align != 0 {
740+
stack_ptr += 1;
741+
}
726742
let my_ptr = stack_ptr;
727743
stack_ptr += size;
728744
Ok((id, Interval { addr: Stack(my_ptr), size }))
@@ -1469,8 +1485,8 @@ impl Evaluator<'_> {
14691485
Ok(match &c.interned {
14701486
ConstScalar::Bytes(v, memory_map) => {
14711487
let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
1472-
let patch_map = memory_map.transform_addresses(|b| {
1473-
let addr = self.heap_allocate(b.len(), 1); // FIXME: align is wrong
1488+
let patch_map = memory_map.transform_addresses(|b, align| {
1489+
let addr = self.heap_allocate(b.len(), align);
14741490
self.write_memory(addr, b)?;
14751491
Ok(addr.to_usize())
14761492
})?;
@@ -1574,7 +1590,24 @@ impl Evaluator<'_> {
15741590
}
15751591
}
15761592

1577-
fn heap_allocate(&mut self, size: usize, _align: usize) -> Address {
1593+
/// A version of `self.size_align_of` which returns error if the type is unsized. `what` argument should
1594+
/// be something that complete this: `error: type {ty} was unsized. {what} should be sized`
1595+
fn size_align_of_sized(
1596+
&self,
1597+
ty: &Ty,
1598+
locals: &Locals<'_>,
1599+
what: &'static str,
1600+
) -> Result<(usize, usize)> {
1601+
match self.size_align_of(ty, locals)? {
1602+
Some(x) => Ok(x),
1603+
None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
1604+
}
1605+
}
1606+
1607+
fn heap_allocate(&mut self, size: usize, align: usize) -> Address {
1608+
while self.heap.len() % align != 0 {
1609+
self.heap.push(0);
1610+
}
15781611
let pos = self.heap.len();
15791612
self.heap.extend(iter::repeat(0).take(size));
15801613
Address::Heap(pos)

0 commit comments

Comments
 (0)