Skip to content

Commit a9e2a2a

Browse files
committed
Auto merge of #157954 - teor2345:fn-arg-splat-perf-fndef-bitpack, r=<try>
perf: Store 1 bit for splatting in FnDeclFlags
2 parents 9d862dd + 220bdc0 commit a9e2a2a

6 files changed

Lines changed: 120 additions & 74 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3058,23 +3058,33 @@ impl FnDecl {
30583058
}
30593059

30603060
/// The marker index for "no splatted arguments".
3061-
/// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`.
3061+
/// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX`.
3062+
// FIXME(splat): if we remove this limit from hir::FnDecl and FnSig, all instances of this
3063+
// constant can go away entirely.
30623064
pub const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX;
30633065

3064-
/// Returns a splatted argument index, if any are present.
3065-
pub fn splatted(&self) -> Option<u16> {
3066-
self.inputs.iter().enumerate().find_map(|(index, arg)| {
3067-
if index == Self::NO_SPLATTED_ARG_INDEX as usize {
3068-
// AST validation has already checked the splatted argument index is valid, so just
3069-
// ignore invalid indexes here.
3070-
None
3071-
} else {
3072-
arg.attrs
3073-
.iter()
3074-
.any(|attr| attr.has_name(sym::splat))
3075-
.then_some(u16::try_from(index).unwrap())
3076-
}
3077-
})
3066+
/// Returns a splatted argument index and its span, if any splatted arguments are present.
3067+
#[inline]
3068+
pub fn splatted(&self) -> Option<(u16 /* arg_index */, Span)> {
3069+
let (index, span) = self.inputs.iter().enumerate().find_map(|(index, arg)| {
3070+
arg.attrs.iter().find_map(|attr| {
3071+
attr.has_name(sym::splat).then_some((u16::try_from(index).unwrap(), attr.span))
3072+
})
3073+
})?;
3074+
3075+
if index == Self::NO_SPLATTED_ARG_INDEX {
3076+
// AST validation has already checked the splatted argument index is valid, so just
3077+
// ignore invalid indexes here.
3078+
None
3079+
} else {
3080+
Some((index, span))
3081+
}
3082+
}
3083+
3084+
/// Returns `true` if the function has a splatted argument.
3085+
#[inline(always)]
3086+
pub fn has_splatted_arg(&self) -> bool {
3087+
self.splatted().is_some()
30783088
}
30793089
}
30803090

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@ struct ParamInfo {
9393
/// Whether the function arguments end in a C variadic `...` parameter.
9494
pub c_variadic: bool,
9595

96-
/// The index of the splatted parameter, if any.
97-
pub splatted: Option<u16>,
96+
/// Does the function have a splatted parameter?
97+
/// The index is available from the attributes.
98+
pub has_splatted_arg: bool,
9899
}
99100

100101
const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO;
@@ -384,15 +385,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
384385
self.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id())
385386
}
386387

387-
/// Returns function parameter info, including C variadic `...` and `#[splat]` if present.
388+
/// Returns function parameter info, including C variadic `...` and `#[splat]` flag if present.
388389
fn param_info(&self, def_id: DefId) -> ParamInfo {
389390
let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder();
390391

391-
// FIXME(splat): use `sig.splatted()` once FnSig has it
392+
// FIXME(splat): use `sig.splatted().is_some()` once FnSig has it
392393
ParamInfo {
393394
param_count: sig.inputs().len() + usize::from(sig.c_variadic()),
394395
c_variadic: sig.c_variadic(),
395-
splatted: None,
396+
has_splatted_arg: false,
396397
}
397398
}
398399

@@ -407,7 +408,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
407408
call_expr_id: HirId,
408409
unused_target_expr: bool,
409410
) -> &'hir hir::FnDecl<'hir> {
410-
let ParamInfo { param_count, c_variadic, splatted } = param_info;
411+
let ParamInfo { param_count, c_variadic, has_splatted_arg } = param_info;
411412

412413
// The last parameter in C variadic functions is skipped in the signature,
413414
// like during regular lowering.
@@ -454,8 +455,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
454455
fn_decl_kind: FnDeclFlags::default()
455456
.set_lifetime_elision_allowed(true)
456457
.set_c_variadic(c_variadic)
457-
.set_splatted(splatted, inputs.len())
458-
.unwrap(),
458+
.set_has_splatted_arg(has_splatted_arg),
459459
})
460460
}
461461

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,15 +1776,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
17761776
coro: Option<CoroutineKind>,
17771777
) -> &'hir hir::FnDecl<'hir> {
17781778
let c_variadic = decl.c_variadic();
1779-
let mut splatted = decl.splatted();
1779+
let mut splatted_arg_index = decl.splatted();
17801780

17811781
// Skip the `...` (`CVarArgs`) trailing arguments from the AST,
17821782
// as they are not explicit in HIR/Ty function signatures.
17831783
// (instead, the `c_variadic` flag is set to `true`)
17841784
let mut inputs = &decl.inputs[..];
17851785
if decl.c_variadic() {
17861786
// Splat + variadic errors in AST validation, so just ignore one of them here.
1787-
splatted = None;
1787+
splatted_arg_index = None;
17881788
inputs = &inputs[..inputs.len() - 1];
17891789
}
17901790
let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
@@ -1875,8 +1875,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
18751875
self.owner.id == fn_node_id && self.owner.lifetime_elision_allowed,
18761876
)
18771877
.set_c_variadic(c_variadic)
1878-
.set_splatted(splatted, inputs.len())
1879-
.unwrap();
1878+
.set_has_splatted_arg(splatted_arg_index.is_some());
1879+
1880+
if let Some((index, span)) = splatted_arg_index {
1881+
// For performance, just lower the one attribute fn args care about to HIR.
1882+
let local_id = inputs[usize::from(index)].hir_id.local_id;
1883+
assert!(!self.attrs.contains_key(&local_id));
1884+
self.attrs.insert(
1885+
local_id,
1886+
arena_vec![self; hir::Attribute::Parsed(hir::attrs::AttributeKind::Splat(span))],
1887+
);
1888+
}
18801889

18811890
self.arena.alloc(hir::FnDecl { inputs, output, fn_decl_kind })
18821891
}

compiler/rustc_hir/src/hir.rs

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use tracing::debug;
3636
use crate::attrs::AttributeKind;
3737
use crate::def::{CtorKind, DefKind, MacroKinds, PerNS, Res};
3838
use crate::def_id::{DefId, LocalDefIdMap};
39+
use crate::find_attr;
3940
pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
4041
use crate::intravisit::{FnKind, VisitorExt};
4142
use crate::lints::DelayedLints;
@@ -4034,7 +4035,8 @@ pub enum SplattedArgIndexError {
40344035
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40354036
#[derive(Encodable, Decodable, StableHash)]
40364037
pub struct FnDeclFlags {
4037-
/// Holds the c_variadic and lifetime_elision_allowed bitflags, and 3 bits for the `ImplicitSelfKind`.
4038+
/// Holds the c_variadic, lifetime_elision_allowed, and has_splatted_arg bitflags, and 3 bits
4039+
/// for the `ImplicitSelfKind`.
40384040
flags: u8,
40394041

40404042
/// Which function argument is splatted into multiple arguments in callers, if any?
@@ -4058,8 +4060,8 @@ impl fmt::Debug for FnDeclFlags {
40584060
f.field(&"CVariadic");
40594061
}
40604062

4061-
if let Some(index) = self.splatted() {
4062-
f.field(&format!("Splatted({})", index));
4063+
if self.has_splatted_arg() {
4064+
f.field(&"HasSplattedArg");
40634065
}
40644066

40654067
f.finish()
@@ -4076,20 +4078,19 @@ impl FnDeclFlags {
40764078
/// Bitflag for lifetime elision.
40774079
const LIFETIME_ELISION_ALLOWED_FLAG: u8 = 1 << 4;
40784080

4079-
/// Marker index for "no splatted argument".
4080-
/// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`.
4081-
const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX;
4081+
/// Bitflag set if any argument is splatted (for performance).
4082+
const HAS_SPLATTED_ARG_FLAG: u8 = 1 << 5;
40824083

40834084
/// Create a new FnDeclKind with no implicit self, no lifetime elision, no C-style variadic
4084-
/// argument, and no splatting.
4085+
/// argument, and no splatted argument.
40854086
/// To modify these flags, use the `set_*` methods, for readability.
40864087
// FIXME: use Default instead when that trait is const stable.
40874088
pub const fn default() -> Self {
40884089
Self { flags: 0, splatted: 0 }
40894090
.set_implicit_self(ImplicitSelfKind::None)
40904091
.set_lifetime_elision_allowed(false)
40914092
.set_c_variadic(false)
4092-
.set_no_splatted_args()
4093+
.set_has_splatted_arg(false)
40934094
}
40944095

40954096
/// Set the implicit self kind.
@@ -4132,38 +4133,15 @@ impl FnDeclFlags {
41324133
self
41334134
}
41344135

4135-
/// Set the splatted argument index.
4136-
/// The number of function arguments is used for error checking.
4136+
/// Set the splatted argument flag.
41374137
#[must_use = "this method does not modify the receiver"]
4138-
pub const fn set_splatted(
4139-
mut self,
4140-
splatted: Option<u16>,
4141-
args_len: usize,
4142-
) -> Result<Self, SplattedArgIndexError> {
4143-
if let Some(splatted_arg_index) = splatted {
4144-
if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX {
4145-
// This index value is used as a marker for "no splatting", so it is unsupported.
4146-
return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index });
4147-
} else if splatted_arg_index as usize >= args_len {
4148-
return Err(SplattedArgIndexError::OutOfBounds {
4149-
splatted_arg_index,
4150-
args_len: args_len as u16,
4151-
});
4152-
}
4153-
4154-
self.splatted = splatted_arg_index;
4138+
pub const fn set_has_splatted_arg(mut self, has_splatted_arg: bool) -> Self {
4139+
if has_splatted_arg {
4140+
self.flags |= Self::HAS_SPLATTED_ARG_FLAG;
41554141
} else {
4156-
self.splatted = Self::NO_SPLATTED_ARG_INDEX;
4142+
self.flags &= !Self::HAS_SPLATTED_ARG_FLAG;
41574143
}
41584144

4159-
Ok(self)
4160-
}
4161-
4162-
/// Set "no splatted arguments" for the function declaration.
4163-
#[must_use = "this method does not modify the receiver"]
4164-
pub const fn set_no_splatted_args(mut self) -> Self {
4165-
self.splatted = Self::NO_SPLATTED_ARG_INDEX;
4166-
41674145
self
41684146
}
41694147

@@ -4189,9 +4167,38 @@ impl FnDeclFlags {
41894167
self.flags & Self::LIFETIME_ELISION_ALLOWED_FLAG != 0
41904168
}
41914169

4192-
/// Get the splatted argument index, if any.
4193-
pub const fn splatted(self) -> Option<u16> {
4194-
if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) }
4170+
/// Does this function have a splatted argument?
4171+
pub const fn has_splatted_arg(self) -> bool {
4172+
self.flags & Self::HAS_SPLATTED_ARG_FLAG != 0
4173+
}
4174+
4175+
/// Returns `true` if the given input contains a `#[splat]` attribute in `attrs`.
4176+
pub fn is_splatted_arg<'hir>(
4177+
&self,
4178+
input: &'hir Ty<'hir>,
4179+
attrs: &'hir dyn Fn(HirId) -> &'hir [Attribute],
4180+
) -> bool {
4181+
self.has_splatted_arg() && find_attr!(attrs(input.hir_id), Splat(_))
4182+
}
4183+
4184+
/// Searches `inputs` and `attrs` for the index of the splatted argument. Returns `None` if
4185+
/// there is no splatted argument.
4186+
pub fn splatted_arg_index<'hir>(
4187+
&self,
4188+
inputs: &'hir [Ty<'hir>],
4189+
attrs: &'hir dyn Fn(HirId) -> &'hir [Attribute],
4190+
) -> Option<u16> {
4191+
if !self.has_splatted_arg() {
4192+
return None;
4193+
}
4194+
4195+
for (index, input) in inputs.iter().enumerate() {
4196+
if self.is_splatted_arg(input, attrs) {
4197+
return Some(u16::try_from(index).unwrap());
4198+
}
4199+
}
4200+
4201+
unreachable!("no splatted argument found");
41954202
}
41964203
}
41974204

@@ -4240,8 +4247,27 @@ impl<'hir> FnDecl<'hir> {
42404247
self.fn_decl_kind.lifetime_elision_allowed()
42414248
}
42424249

4243-
pub fn splatted(&self) -> Option<u16> {
4244-
self.fn_decl_kind.splatted()
4250+
/// Returns `true` if the function has a splatted argument.
4251+
pub fn has_splatted_arg(&self) -> bool {
4252+
self.fn_decl_kind.has_splatted_arg()
4253+
}
4254+
4255+
/// Returns `true` if the given argument `index` contains a `#[splat]` attribute in `attrs`.
4256+
pub fn is_splatted_arg(
4257+
&self,
4258+
index: usize,
4259+
attrs: &'hir dyn Fn(HirId) -> &'hir [Attribute],
4260+
) -> bool {
4261+
self.fn_decl_kind.is_splatted_arg(&self.inputs[index], attrs)
4262+
}
4263+
4264+
/// Searches `self.inputs` and `attrs` for the index of the splatted argument. Returns `None`
4265+
/// if there is no splatted argument.
4266+
pub fn splatted_arg_index(
4267+
&self,
4268+
attrs: &'hir dyn Fn(HirId) -> &'hir [Attribute],
4269+
) -> Option<u16> {
4270+
self.fn_decl_kind.splatted_arg_index(self.inputs, attrs)
42454271
}
42464272

42474273
pub fn dummy(span: Span) -> Self {

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3501,7 +3501,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
35013501
debug!(?output_ty);
35023502

35033503
debug!(?abi, ?safety, ?decl.fn_decl_kind, input_tys_len = ?input_tys.len());
3504-
// FIXME(splat): use `set_splatted()` once FnSig has it
3504+
// FIXME(splat): use ```
3505+
// set_splatted(decl.splatted_arg_index(&move |id| HasAttrs::get_attrs(id, &tcx)), input_tys.len())
3506+
// ``` once FnSig has it
35053507
let fn_sig_kind = FnSigKind::default()
35063508
.set_abi(abi)
35073509
.set_safety(safety)

compiler/rustc_hir_pretty/src/lib.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
1616
use rustc_ast_pretty::pp::{self, BoxMarker, Breaks};
1717
use rustc_ast_pretty::pprust::state::MacHeader;
1818
use rustc_ast_pretty::pprust::{Comments, PrintState};
19-
use rustc_hir as hir;
2019
use rustc_hir::attrs::{AttributeKind, PrintAttribute};
2120
use rustc_hir::{
22-
BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg, GenericBound,
23-
GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind,
24-
PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind,
21+
self as hir, BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg,
22+
GenericBound, GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node,
23+
PatKind, PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind, find_attr,
2524
};
2625
use rustc_span::source_map::SourceMap;
2726
use rustc_span::{DUMMY_SP, FileName, Ident, Span, Spanned, Symbol, kw, sym};
@@ -2265,7 +2264,7 @@ impl<'a> State<'a> {
22652264
assert!(arg_idents.is_empty() || body_id.is_none());
22662265
let mut i = 0;
22672266
let mut print_arg = |s: &mut Self, ty: Option<&hir::Ty<'_>>| {
2268-
if Some(i) == decl.splatted().map(usize::from) {
2267+
if decl.has_splatted_arg() && find_attr!(s.attrs(decl.inputs[i].hir_id), Splat(_)) {
22692268
s.word("#[splat]");
22702269
}
22712270
if i == 0 && decl.implicit_self().has_implicit_self() {

0 commit comments

Comments
 (0)