Skip to content

Commit be76b42

Browse files
committed
Simplify get discriminant codegen for simple cases
Most often all niche-encoded tags are at the end of tag scalar range. In these cases we can `<` on tag directly to know whether `is_untagged`. Also, avoiding casting and performing arithmetics directly on tag's type seem to help optimizations.
1 parent 17bcac1 commit be76b42

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

compiler/rustc_codegen_ssa/src/mir/place.rs

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use rustc_middle::mir;
99
use rustc_middle::mir::tcx::PlaceTy;
1010
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
1111
use rustc_middle::ty::{self, Ty};
12-
use rustc_target::abi::{Abi, Align, FieldsShape, Int, TagEncoding};
13-
use rustc_target::abi::{VariantIdx, Variants};
12+
use rustc_target::abi::{Abi, Align, FieldsShape, Int, Integer, TagEncoding};
13+
use rustc_target::abi::{Primitive, Scalar, VariantIdx, Variants};
1414

1515
#[derive(Copy, Clone, Debug)]
1616
pub struct PlaceRef<'tcx, V> {
@@ -265,9 +265,46 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
265265
bx.cx().const_uint(cast_to, untagged_variant.as_u32() as u64),
266266
)
267267
}
268+
// Handle other non-patological cases (= without niche wraparound, and with
269+
// niche_variants.end fitting in the tag type).
270+
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }
271+
if is_niche_after_nonniche(tag_scalar, niche_start, niche_variants, bx.cx()) =>
272+
{
273+
let is_untagged = bx.icmp(
274+
IntPredicate::IntULT,
275+
tag,
276+
bx.cx().const_uint_big(niche_llty, niche_start),
277+
);
278+
279+
let first_niche_variant = niche_variants.start().as_u32() as u128;
280+
let niche_discr = match first_niche_variant.cmp(&niche_start) {
281+
std::cmp::Ordering::Less => {
282+
let diff = niche_start - first_niche_variant;
283+
let diff = bx.cx().const_uint_big(niche_llty, diff);
284+
bx.unchecked_usub(tag, diff)
285+
}
286+
std::cmp::Ordering::Equal => tag,
287+
std::cmp::Ordering::Greater => {
288+
let diff = first_niche_variant as u64 - niche_start as u64;
289+
let diff = bx.cx().const_uint(niche_llty, diff);
290+
bx.unchecked_uadd(tag, diff)
291+
}
292+
};
293+
294+
let niche_discr = bx.sub(tag, diff);
295+
let discr = bx.select(
296+
is_untagged,
297+
bx.cx().const_uint(niche_llty, untagged_variant.as_u32() as u64),
298+
niche_discr,
299+
);
300+
301+
bx.intcast(discr, cast_to, false)
302+
}
268303
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
269-
// Assumption: here, llty should not be pointer.
270-
// FIXME(eddyb) check the actual primitive type here.
304+
if matches!(tag_scalar.primitive(), Primitive::Pointer) {
305+
// arith and non-null constants won't work on pointers
306+
bug!("pointer with more than one niche variant")
307+
}
271308

272309
// Rebase from niche values to discriminants, and check
273310
// whether the result is in range for the niche variants.
@@ -552,3 +589,33 @@ fn round_up_const_value_to_alignment<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
552589
let offset = bx.and(neg_value, align_minus_1);
553590
bx.add(value, offset)
554591
}
592+
593+
/// Are all niche variants encoded in tag greater than the actual untagged tag values (so that we
594+
/// can avoid the "relative discriminant" and we can simply `< niche_start` to ask whether it's
595+
/// untagged or not). Also check if last _variant_ value fits in the _tag_'s type.
596+
// Perhaps we can store this as flag in TagEncoding::Niche instead of recomputing?
597+
fn is_niche_after_nonniche(
598+
tag: Scalar,
599+
niche_start: u128,
600+
niche_variants: &std::ops::RangeInclusive<VariantIdx>,
601+
cx: &impl rustc_target::abi::HasDataLayout,
602+
) -> bool {
603+
let tag_range = tag.valid_range(cx);
604+
if tag_range.wraps() {
605+
return false;
606+
}
607+
let n_variants = niche_variants.end().as_u32() - niche_variants.start().as_u32();
608+
match tag.primitive() {
609+
Int(int, _) => {
610+
if niche_start.checked_add(n_variants as u128) != Some(tag_range.end) {
611+
return false;
612+
}
613+
match int {
614+
Integer::I8 if niche_variants.end().as_u32() > u8::MAX as u32 => false,
615+
Integer::I16 if niche_variants.end().as_u32() > u16::MAX as u32 => false,
616+
_ => true,
617+
}
618+
}
619+
_ => false,
620+
}
621+
}

compiler/rustc_target/src/abi/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,11 @@ impl WrappingRange {
819819
debug_assert!(self.start <= max_value && self.end <= max_value);
820820
self.start == (self.end.wrapping_add(1) & max_value)
821821
}
822+
823+
#[inline]
824+
pub fn wraps(&self) -> bool {
825+
return self.start > self.end;
826+
}
822827
}
823828

824829
impl fmt::Debug for WrappingRange {

0 commit comments

Comments
 (0)