Skip to content

Commit 8ab84c7

Browse files
committed
abi: more workarounds for old-style #[repr(simd)] structs.
1 parent 3930056 commit 8ab84c7

File tree

2 files changed

+97
-6
lines changed

2 files changed

+97
-6
lines changed

crates/rustc_codegen_spirv/build.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,16 @@ mod win {",
199199
}
200200
if relative_path == Path::new("src/back/metadata.rs") {
201201
// HACK(eddyb) remove `object` dependency.
202-
src = src.replace("
203-
pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {","
202+
src = src.replace(
203+
"
204+
pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {",
205+
"
204206
pub(crate) fn create_object_file(_: &Session) -> Option<write::Object<'static>> {
205207
None
206208
}
207209
#[cfg(any())]
208-
pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {");
210+
pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {",
211+
);
209212
}
210213

211214
// HACK(eddyb) "typed alloca" patches.
@@ -225,8 +228,16 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
225228
} else if relative_path == Path::new("src/mir/operand.rs") {
226229
src = src.replace("alloca(field.size,", "typed_alloca(llfield_ty,");
227230

228-
// HACK(eddyb) non-array `#[repr(simd)]` workaround (see `src/abi.rs`).
231+
// HACK(eddyb) non-array `#[repr(simd)]` workarounds (see `src/abi.rs`).
229232
src = src.replace("if constant_ty.is_simd() {", "if false {");
233+
src = src.replace(
234+
"match (self.val, self.layout.backend_repr) {",
235+
"match (self.val, self.layout.backend_repr) {
236+
// `#[repr(simd)]` types are also immediate.
237+
(OperandValue::Immediate(llval), BackendRepr::SimdVector { element, .. }) => {
238+
(Some(element), bx.extract_element(llval, bx.cx().const_usize(i as u64)))
239+
}",
240+
);
230241
}
231242

232243
fs::write(out_path, src)?;

crates/rustc_codegen_spirv/src/abi.rs

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ use itertools::Itertools;
88
use rspirv::spirv::{Dim, ImageFormat, StorageClass, Word};
99
use rustc_abi::ExternAbi as Abi;
1010
use rustc_abi::{
11-
Align, BackendRepr, FieldIdx, FieldsShape, LayoutData, Primitive, ReprFlags, ReprOptions,
12-
Scalar, Size, TagEncoding, VariantIdx, Variants,
11+
Align, BackendRepr, FieldIdx, FieldsShape, HasDataLayout as _, LayoutData, Primitive,
12+
ReprFlags, ReprOptions, Scalar, Size, TagEncoding, VariantIdx, Variants,
1313
};
1414
use rustc_data_structures::fx::FxHashMap;
1515
use rustc_errors::ErrorGuaranteed;
16+
use rustc_hashes::Hash64;
1617
use rustc_index::Idx;
1718
use rustc_middle::query::Providers;
1819
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
@@ -164,6 +165,85 @@ pub(crate) fn provide(providers: &mut Providers) {
164165
}
165166

166167
providers.layout_of = |tcx, key| {
168+
// HACK(eddyb) to special-case any types at all, they must be normalized,
169+
// but when normalization would be needed, `layout_of`'s default provider
170+
// recurses (supposedly for caching reasons), i.e. its calls `layout_of`
171+
// w/ the normalized type in input, which once again reaches this hook,
172+
// without ever needing any explicit normalization here.
173+
let ty = key.value;
174+
175+
// HACK(eddyb) bypassing upstream `#[repr(simd)]` changes (see also
176+
// the later comment above `check_well_formed`, for more details).
177+
let reimplement_old_style_repr_simd = match ty.kind() {
178+
ty::Adt(def, args) if def.repr().simd() && !def.repr().packed() && def.is_struct() => {
179+
Some(def.non_enum_variant()).and_then(|v| {
180+
let (count, e_ty) = v
181+
.fields
182+
.iter()
183+
.map(|f| f.ty(tcx, args))
184+
.dedup_with_count()
185+
.exactly_one()
186+
.ok()?;
187+
let e_len = u64::try_from(count).ok().filter(|&e_len| e_len > 1)?;
188+
Some((def, e_ty, e_len))
189+
})
190+
}
191+
_ => None,
192+
};
193+
194+
// HACK(eddyb) tweaked copy of the old upstream logic for `#[repr(simd)]`:
195+
// https://github.com/rust-lang/rust/blob/1.86.0/compiler/rustc_ty_utils/src/layout.rs#L464-L590
196+
if let Some((adt_def, e_ty, e_len)) = reimplement_old_style_repr_simd {
197+
let cx = rustc_middle::ty::layout::LayoutCx::new(
198+
tcx,
199+
key.typing_env.with_post_analysis_normalized(tcx),
200+
);
201+
let dl = cx.data_layout();
202+
203+
// Compute the ABI of the element type:
204+
let e_ly = cx.layout_of(e_ty)?;
205+
let BackendRepr::Scalar(e_repr) = e_ly.backend_repr else {
206+
// This error isn't caught in typeck, e.g., if
207+
// the element type of the vector is generic.
208+
tcx.dcx().span_fatal(
209+
tcx.def_span(adt_def.did()),
210+
format!(
211+
"SIMD type `{ty}` with a non-primitive-scalar \
212+
(integer/float/pointer) element type `{}`",
213+
e_ly.ty
214+
),
215+
);
216+
};
217+
218+
// Compute the size and alignment of the vector:
219+
let size = e_ly.size.checked_mul(e_len, dl).unwrap();
220+
let align = dl.llvmlike_vector_align(size);
221+
let size = size.align_to(align.abi);
222+
223+
let layout = tcx.mk_layout(LayoutData {
224+
variants: Variants::Single {
225+
index: rustc_abi::FIRST_VARIANT,
226+
},
227+
fields: FieldsShape::Array {
228+
stride: e_ly.size,
229+
count: e_len,
230+
},
231+
backend_repr: BackendRepr::SimdVector {
232+
element: e_repr,
233+
count: e_len,
234+
},
235+
largest_niche: e_ly.largest_niche,
236+
uninhabited: false,
237+
size,
238+
align,
239+
max_repr_align: None,
240+
unadjusted_abi_align: align.abi,
241+
randomization_seed: e_ly.randomization_seed.wrapping_add(Hash64::new(e_len)),
242+
});
243+
244+
return Ok(TyAndLayout { ty, layout });
245+
}
246+
167247
let TyAndLayout { ty, mut layout } =
168248
(rustc_interface::DEFAULT_QUERY_PROVIDERS.layout_of)(tcx, key)?;
169249

0 commit comments

Comments
 (0)