mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-06 04:08:40 +00:00
rustc_codegen_ssa: Fix for codegen_get_discr
When doing the optimized implementation of getting the discriminant, the arithmetic needs to be done in the tag type so wrapping behavior works correctly. Fixes #104519
This commit is contained in:
parent
fd3bfb3551
commit
31c0645b9d
@ -309,14 +309,14 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||||||
// In the algorithm above, we can change
|
// In the algorithm above, we can change
|
||||||
// cast(relative_tag) + niche_variants.start()
|
// cast(relative_tag) + niche_variants.start()
|
||||||
// into
|
// into
|
||||||
// cast(tag) + (niche_variants.start() - niche_start)
|
// cast(tag + (niche_variants.start() - niche_start))
|
||||||
// if either the casted type is no larger than the original
|
// if either the casted type is no larger than the original
|
||||||
// type, or if the niche values are contiguous (in either the
|
// type, or if the niche values are contiguous (in either the
|
||||||
// signed or unsigned sense).
|
// signed or unsigned sense).
|
||||||
let can_incr_after_cast = cast_smaller || niches_ule || niches_sle;
|
let can_incr = cast_smaller || niches_ule || niches_sle;
|
||||||
|
|
||||||
let data_for_boundary_niche = || -> Option<(IntPredicate, u128)> {
|
let data_for_boundary_niche = || -> Option<(IntPredicate, u128)> {
|
||||||
if !can_incr_after_cast {
|
if !can_incr {
|
||||||
None
|
None
|
||||||
} else if niche_start == low_unsigned {
|
} else if niche_start == low_unsigned {
|
||||||
Some((IntPredicate::IntULE, niche_end))
|
Some((IntPredicate::IntULE, niche_end))
|
||||||
@ -353,24 +353,33 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||||||
// The algorithm is now this:
|
// The algorithm is now this:
|
||||||
// is_niche = tag <= niche_end
|
// is_niche = tag <= niche_end
|
||||||
// discr = if is_niche {
|
// discr = if is_niche {
|
||||||
// cast(tag) + (niche_variants.start() - niche_start)
|
// cast(tag + (niche_variants.start() - niche_start))
|
||||||
// } else {
|
// } else {
|
||||||
// untagged_variant
|
// untagged_variant
|
||||||
// }
|
// }
|
||||||
// (the first line may instead be tag >= niche_start,
|
// (the first line may instead be tag >= niche_start,
|
||||||
// and may be a signed or unsigned comparison)
|
// and may be a signed or unsigned comparison)
|
||||||
|
// The arithmetic must be done before the cast, so we can
|
||||||
|
// have the correct wrapping behavior. See issue #104519 for
|
||||||
|
// the consequences of getting this wrong.
|
||||||
let is_niche =
|
let is_niche =
|
||||||
bx.icmp(predicate, tag, bx.cx().const_uint_big(tag_llty, constant));
|
bx.icmp(predicate, tag, bx.cx().const_uint_big(tag_llty, constant));
|
||||||
let cast_tag = if cast_smaller {
|
let delta = (niche_variants.start().as_u32() as u128).wrapping_sub(niche_start);
|
||||||
bx.intcast(tag, cast_to, false)
|
let incr_tag = if delta == 0 {
|
||||||
} else if niches_ule {
|
tag
|
||||||
bx.zext(tag, cast_to)
|
|
||||||
} else {
|
} else {
|
||||||
bx.sext(tag, cast_to)
|
bx.add(tag, bx.cx().const_uint_big(tag_llty, delta))
|
||||||
};
|
};
|
||||||
|
|
||||||
let delta = (niche_variants.start().as_u32() as u128).wrapping_sub(niche_start);
|
let cast_tag = if cast_smaller {
|
||||||
(is_niche, cast_tag, delta)
|
bx.intcast(incr_tag, cast_to, false)
|
||||||
|
} else if niches_ule {
|
||||||
|
bx.zext(incr_tag, cast_to)
|
||||||
|
} else {
|
||||||
|
bx.sext(incr_tag, cast_to)
|
||||||
|
};
|
||||||
|
|
||||||
|
(is_niche, cast_tag, 0)
|
||||||
} else {
|
} else {
|
||||||
// The special cases don't apply, so we'll have to go with
|
// The special cases don't apply, so we'll have to go with
|
||||||
// the general algorithm.
|
// the general algorithm.
|
||||||
|
@ -34,11 +34,8 @@ pub enum Enum1 {
|
|||||||
|
|
||||||
// CHECK: define i8 @match1{{.*}}
|
// CHECK: define i8 @match1{{.*}}
|
||||||
// CHECK-NEXT: start:
|
// CHECK-NEXT: start:
|
||||||
// CHECK-NEXT: %1 = icmp ugt i8 %0, 1
|
// CHECK-NEXT: %1 = {{.*}}call i8 @llvm.usub.sat.i8(i8 %0, i8 1)
|
||||||
// CHECK-NEXT: %2 = zext i8 %0 to i64
|
// CHECK-NEXT: switch i8 %1, label {{.*}} [
|
||||||
// CHECK-NEXT: %3 = add nsw i64 %2, -1
|
|
||||||
// CHECK-NEXT: %_2 = select i1 %1, i64 %3, i64 0
|
|
||||||
// CHECK-NEXT: switch i64 %_2, label {{.*}} [
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn match1(e: Enum1) -> u8 {
|
pub fn match1(e: Enum1) -> u8 {
|
||||||
use Enum1::*;
|
use Enum1::*;
|
||||||
|
36
src/test/ui/enum-discriminant/issue-104519.rs
Normal file
36
src/test/ui/enum-discriminant/issue-104519.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// run-pass
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
enum OpenResult {
|
||||||
|
Ok(()),
|
||||||
|
Err(()),
|
||||||
|
TransportErr(TransportErr),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(i32)]
|
||||||
|
enum TransportErr {
|
||||||
|
UnknownMethod = -2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn some_match(result: OpenResult) -> u8 {
|
||||||
|
match result {
|
||||||
|
OpenResult::Ok(()) => 0,
|
||||||
|
_ => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let result = OpenResult::Ok(());
|
||||||
|
assert_eq!(some_match(result), 0);
|
||||||
|
|
||||||
|
let result = OpenResult::Ok(());
|
||||||
|
match result {
|
||||||
|
OpenResult::Ok(()) => (),
|
||||||
|
_ => unreachable!("message a"),
|
||||||
|
}
|
||||||
|
match result {
|
||||||
|
OpenResult::Ok(()) => (),
|
||||||
|
_ => unreachable!("message b"),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user