Skip to content

Commit c668b83

Browse files
committed
Use a ArrayVec in CastTarget
This commit switches a fixed-size list of `[Option<Reg>; 8]` to instead holding `ArrayVec<Reg, 8>` in the `CastTarget` type used when calculating ABIs. This is inspired by [discussion on Zulip][link] where I'm hoping to in the near future extend the usage of this to possibly beyond 8 elements for a new WebAssembly ABI taking advantage of multi-value. For now though this mostly just switches to array/slice-like idioms of accessors rather than dealing with `Option<Reg>` as the unit. [link]: https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/Using.20.60ArgAbi.3A.3Amake_direct_deprecated.60/with/598607139
1 parent 77a4fb6 commit c668b83

13 files changed

Lines changed: 58 additions & 77 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4756,6 +4756,7 @@ dependencies = [
47564756
name = "rustc_target"
47574757
version = "0.0.0"
47584758
dependencies = [
4759+
"arrayvec",
47594760
"bitflags",
47604761
"object 0.37.3",
47614762
"rustc_abi",

compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ fn apply_attrs_to_abi_param(param: AbiParam, arg_attrs: ArgAttributes) -> AbiPar
4444

4545
fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[(Size, AbiParam); 2]> {
4646
if let Some(offset_from_start) = cast.rest_offset {
47-
assert!(cast.prefix[1..].iter().all(|p| p.is_none()));
47+
assert_eq!(cast.prefix.len(), 1);
4848
assert_eq!(cast.rest.unit.size, cast.rest.total);
49-
let first = cast.prefix[0].unwrap();
49+
let first = cast.prefix[0];
5050
let second = cast.rest.unit;
5151
return smallvec![
5252
(Size::ZERO, reg_to_abi_param(first)),
@@ -71,7 +71,6 @@ fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[(Size, AbiParam); 2
7171
let args = cast
7272
.prefix
7373
.iter()
74-
.flatten()
7574
.map(|&reg| reg_to_abi_param(reg))
7675
.chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit)));
7776

compiler/rustc_codegen_gcc/src/abi.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl GccType for CastTarget {
4646
)
4747
};
4848

49-
if self.prefix.iter().all(|x| x.is_none()) {
49+
if self.prefix.is_empty() {
5050
// Simplify to a single unit when there is no prefix and size <= unit size
5151
if self.rest.total <= self.rest.unit.size {
5252
return rest_gcc_unit;
@@ -62,7 +62,7 @@ impl GccType for CastTarget {
6262
let mut args: Vec<_> = self
6363
.prefix
6464
.iter()
65-
.flat_map(|option_reg| option_reg.map(|reg| reg.gcc_type(cx)))
65+
.map(|reg| reg.gcc_type(cx))
6666
.chain((0..rest_count).map(|_| rest_gcc_unit))
6767
.collect();
6868

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ impl LlvmType for CastTarget {
187187

188188
// Simplify to a single unit or an array if there's no prefix.
189189
// This produces the same layout, but using a simpler type.
190-
if self.prefix.iter().all(|x| x.is_none()) {
190+
if self.prefix.is_empty() {
191191
// We can't do this if is_consecutive is set and the unit would get
192192
// split on the target. Currently, this is only relevant for i128
193193
// registers.
@@ -199,8 +199,7 @@ impl LlvmType for CastTarget {
199199
}
200200

201201
// Generate a struct type with the prefix and the "rest" arguments.
202-
let prefix_args =
203-
self.prefix.iter().flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx)));
202+
let prefix_args = self.prefix.iter().map(|reg| reg.llvm_type(cx));
204203
let rest_args = (0..rest_count).map(|_| rest_ll_unit);
205204
let args: Vec<_> = prefix_args.chain(rest_args).collect();
206205
cx.type_struct(&args, false)

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2233,9 +2233,9 @@ fn load_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
22332233
) -> Bx::Value {
22342234
let cast_ty = bx.cast_backend_type(cast);
22352235
if let Some(offset_from_start) = cast.rest_offset {
2236-
assert!(cast.prefix[1..].iter().all(|p| p.is_none()));
2236+
assert_eq!(cast.prefix.len(), 1);
22372237
assert_eq!(cast.rest.unit.size, cast.rest.total);
2238-
let first_ty = bx.reg_backend_type(&cast.prefix[0].unwrap());
2238+
let first_ty = bx.reg_backend_type(&cast.prefix[0]);
22392239
let second_ty = bx.reg_backend_type(&cast.rest.unit);
22402240
let first = bx.load(first_ty, ptr, align);
22412241
let second_ptr = bx.inbounds_ptradd(ptr, bx.const_usize(offset_from_start.bytes()));
@@ -2256,9 +2256,8 @@ pub fn store_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
22562256
align: Align,
22572257
) {
22582258
if let Some(offset_from_start) = cast.rest_offset {
2259-
assert!(cast.prefix[1..].iter().all(|p| p.is_none()));
2259+
assert_eq!(cast.prefix.len(), 1);
22602260
assert_eq!(cast.rest.unit.size, cast.rest.total);
2261-
assert!(cast.prefix[0].is_some());
22622261
let first = bx.extract_value(value, 0);
22632262
let second = bx.extract_value(value, 1);
22642263
bx.store(first, ptr, align);

compiler/rustc_codegen_ssa/src/mir/naked_asm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_t
451451
PassMode::Cast { pad_i32, ref cast } => {
452452
// For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);`
453453
assert!(!pad_i32, "not currently used by wasm calling convention");
454-
assert!(cast.prefix[0].is_none(), "no prefix");
454+
assert!(cast.prefix.is_empty(), "no prefix");
455455
assert_eq!(cast.rest.total, arg_abi.layout.size, "single item");
456456

457457
let wrapped_wasm_type = match cast.rest.unit.kind {

compiler/rustc_monomorphize/src/mono_checks/abi_check.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ fn passes_vectors_by_value(mode: &PassMode, repr: &BackendRepr) -> UsesVectorReg
2525
match mode {
2626
PassMode::Ignore | PassMode::Indirect { .. } => UsesVectorRegisters::No,
2727
PassMode::Cast { pad_i32: _, cast }
28-
if cast
29-
.prefix
30-
.iter()
31-
.any(|r| r.is_some_and(|x| matches!(x.kind, RegKind::Vector { .. })))
28+
if cast.prefix.iter().any(|x| matches!(x.kind, RegKind::Vector { .. }))
3229
|| matches!(cast.rest.unit.kind, RegKind::Vector { .. }) =>
3330
{
3431
UsesVectorRegisters::FixedVector

compiler/rustc_target/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2024"
55

66
[dependencies]
77
# tidy-alphabetical-start
8+
arrayvec = { version = "0.7", default-features = false }
89
bitflags = "2.4.1"
910
object = { version = "0.37.0", default-features = false, features = ["elf", "macho"] }
1011
rustc_abi = { path = "../rustc_abi" }

compiler/rustc_target/src/callconv/mips64.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use arrayvec::ArrayVec;
12
use rustc_abi::{
23
BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface,
34
};
@@ -81,8 +82,7 @@ where
8182
{
8283
let dl = cx.data_layout();
8384
let size = arg.layout.size;
84-
let mut prefix = [None; 8];
85-
let mut prefix_index = 0;
85+
let mut prefix = ArrayVec::new();
8686

8787
// Detect need for padding
8888
let align = Ord::clamp(arg.layout.align.abi, dl.i64_align, dl.i128_align);
@@ -107,7 +107,7 @@ where
107107
// doubles not part of another aggregate are passed as floats.
108108
let mut last_offset = Size::ZERO;
109109

110-
for i in 0..arg.layout.fields.count() {
110+
'outer: for i in 0..arg.layout.fields.count() {
111111
let field = arg.layout.field(cx, i);
112112
let offset = arg.layout.fields.offset(i);
113113

@@ -117,19 +117,15 @@ where
117117
if offset.is_aligned(dl.f64_align) {
118118
// Insert enough integers to cover [last_offset, offset)
119119
assert!(last_offset.is_aligned(dl.f64_align));
120-
for _ in 0..((offset - last_offset).bits() / 64)
121-
.min((prefix.len() - prefix_index) as u64)
122-
{
123-
prefix[prefix_index] = Some(Reg::i64());
124-
prefix_index += 1;
120+
for _ in 0..((offset - last_offset).bits() / 64) {
121+
if prefix.try_push(Reg::i64()).is_err() {
122+
break 'outer;
123+
}
125124
}
126125

127-
if prefix_index == prefix.len() {
126+
if prefix.try_push(Reg::f64()).is_err() {
128127
break;
129128
}
130-
131-
prefix[prefix_index] = Some(Reg::f64());
132-
prefix_index += 1;
133129
last_offset = offset + Reg::f64().size;
134130
}
135131
}
@@ -139,7 +135,7 @@ where
139135
};
140136

141137
// Extract first 8 chunks as the prefix
142-
let rest_size = size - Size::from_bytes(8) * prefix_index as u64;
138+
let rest_size = size - Size::from_bytes(8) * prefix.len() as u64;
143139
arg.cast_to_and_pad_i32(
144140
CastTarget::prefixed(prefix, Uniform::new(Reg::i64(), rest_size)),
145141
pad_i32,

compiler/rustc_target/src/callconv/mod.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::{fmt, iter};
22

3+
use arrayvec::ArrayVec;
34
use rustc_abi::{
45
AddressSpace, Align, BackendRepr, CanonAbi, ExternAbi, FieldsShape, HasDataLayout, Primitive,
56
Reg, RegKind, Scalar, Size, TyAbiInterface, TyAndLayout, Variants,
@@ -264,7 +265,11 @@ impl Uniform {
264265
/// (and all data in the padding between the registers is dropped).
265266
#[derive(Clone, PartialEq, Eq, Hash, Debug, StableHash)]
266267
pub struct CastTarget {
267-
pub prefix: [Option<Reg>; 8],
268+
// Note that this is fixed to 8 elements for now as ABIs currently don't
269+
// need anything further beyond that, and when this code was originally
270+
// refactored to use `ArrayVec` it was already using 8, so that stuck
271+
// around.
272+
pub prefix: ArrayVec<Reg, 8>,
268273
/// The offset of `rest` from the start of the value. Currently only implemented for a `Reg`
269274
/// pair created by the `offset_pair` method.
270275
pub rest_offset: Option<Size>,
@@ -280,18 +285,20 @@ impl From<Reg> for CastTarget {
280285

281286
impl From<Uniform> for CastTarget {
282287
fn from(uniform: Uniform) -> CastTarget {
283-
Self::prefixed([None; 8], uniform)
288+
Self::prefixed(Default::default(), uniform)
284289
}
285290
}
286291

287292
impl CastTarget {
288-
pub fn prefixed(prefix: [Option<Reg>; 8], rest: Uniform) -> Self {
293+
pub fn prefixed(prefix: ArrayVec<Reg, 8>, rest: Uniform) -> Self {
289294
Self { prefix, rest_offset: None, rest, attrs: ArgAttributes::new() }
290295
}
291296

292297
pub fn offset_pair(a: Reg, offset_from_start: Size, b: Reg) -> Self {
298+
let mut prefix = ArrayVec::new();
299+
prefix.push(a);
293300
Self {
294-
prefix: [Some(a), None, None, None, None, None, None, None],
301+
prefix,
295302
rest_offset: Some(offset_from_start),
296303
rest: b.into(),
297304
attrs: ArgAttributes::new(),
@@ -304,7 +311,9 @@ impl CastTarget {
304311
}
305312

306313
pub fn pair(a: Reg, b: Reg) -> CastTarget {
307-
Self::prefixed([Some(a), None, None, None, None, None, None, None], Uniform::from(b))
314+
let mut prefix = ArrayVec::new();
315+
prefix.push(a);
316+
Self::prefixed(prefix, Uniform::from(b))
308317
}
309318

310319
/// When you only access the range containing valid data, you can use this unaligned size;
@@ -314,10 +323,7 @@ impl CastTarget {
314323
let prefix_size = if let Some(offset_from_start) = self.rest_offset {
315324
offset_from_start
316325
} else {
317-
self.prefix
318-
.iter()
319-
.filter_map(|x| x.map(|reg| reg.size))
320-
.fold(Size::ZERO, |acc, size| acc + size)
326+
self.prefix.iter().map(|reg| reg.size).fold(Size::ZERO, |acc, size| acc + size)
321327
};
322328
// Remaining arguments are passed in chunks of the unit size
323329
let rest_size =
@@ -333,7 +339,7 @@ impl CastTarget {
333339
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
334340
self.prefix
335341
.iter()
336-
.filter_map(|x| x.map(|reg| reg.align(cx)))
342+
.map(|reg| reg.align(cx))
337343
.fold(cx.data_layout().aggregate_align.max(self.rest.align(cx)), |acc, align| {
338344
acc.max(align)
339345
})

0 commit comments

Comments
 (0)