More discriminant codegen tests
These are from 139729, updated to pass on master.
diff --git a/tests/codegen/enum/enum-discriminant-eq.rs b/tests/codegen/enum/enum-discriminant-eq.rs
new file mode 100644
index 0000000..fd6f349
--- /dev/null
+++ b/tests/codegen/enum/enum-discriminant-eq.rs
@@ -0,0 +1,231 @@
+//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled
+//@ min-llvm-version: 20
+//@ only-64bit
+
+// The `derive(PartialEq)` on enums with field-less variants compares discriminants,
+// so make sure we emit that in some reasonable way.
+
+#![crate_type = "lib"]
+#![feature(ascii_char)]
+#![feature(core_intrinsics)]
+#![feature(repr128)]
+
+use std::ascii::Char as AC;
+use std::cmp::Ordering;
+use std::intrinsics::discriminant_value;
+use std::num::NonZero;
+
+// A type that's bigger than `isize`, unlike the usual cases that have small tags.
+#[repr(u128)]
+pub enum Giant {
+ Two = 2,
+ Three = 3,
+ Four = 4,
+}
+
+#[unsafe(no_mangle)]
+pub fn opt_bool_eq_discr(a: Option<bool>, b: Option<bool>) -> bool {
+ // CHECK-LABEL: @opt_bool_eq_discr(
+ // CHECK: %[[A:.+]] = icmp ne i8 %a, 2
+ // CHECK: %[[B:.+]] = icmp eq i8 %b, 2
+ // CHECK: %[[R:.+]] = xor i1 %[[A]], %[[B]]
+ // CHECK: ret i1 %[[R]]
+
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+#[unsafe(no_mangle)]
+pub fn opt_ord_eq_discr(a: Option<Ordering>, b: Option<Ordering>) -> bool {
+ // CHECK-LABEL: @opt_ord_eq_discr(
+ // CHECK: %[[A:.+]] = icmp ne i8 %a, 2
+ // CHECK: %[[B:.+]] = icmp eq i8 %b, 2
+ // CHECK: %[[R:.+]] = xor i1 %[[A]], %[[B]]
+ // CHECK: ret i1 %[[R]]
+
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+#[unsafe(no_mangle)]
+pub fn opt_nz32_eq_discr(a: Option<NonZero<u32>>, b: Option<NonZero<u32>>) -> bool {
+ // CHECK-LABEL: @opt_nz32_eq_discr(
+ // CHECK: %[[A:.+]] = icmp ne i32 %a, 0
+ // CHECK: %[[B:.+]] = icmp eq i32 %b, 0
+ // CHECK: %[[R:.+]] = xor i1 %[[A]], %[[B]]
+ // CHECK: ret i1 %[[R]]
+
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+#[unsafe(no_mangle)]
+pub fn opt_ac_eq_discr(a: Option<AC>, b: Option<AC>) -> bool {
+ // CHECK-LABEL: @opt_ac_eq_discr(
+ // CHECK: %[[A:.+]] = icmp ne i8 %a, -128
+ // CHECK: %[[B:.+]] = icmp eq i8 %b, -128
+ // CHECK: %[[R:.+]] = xor i1 %[[A]], %[[B]]
+ // CHECK: ret i1 %[[R]]
+
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+#[unsafe(no_mangle)]
+pub fn opt_giant_eq_discr(a: Option<Giant>, b: Option<Giant>) -> bool {
+ // CHECK-LABEL: @opt_giant_eq_discr(
+ // CHECK: %[[A:.+]] = icmp ne i128 %a, 1
+ // CHECK: %[[B:.+]] = icmp eq i128 %b, 1
+ // CHECK: %[[R:.+]] = xor i1 %[[A]], %[[B]]
+ // CHECK: ret i1 %[[R]]
+
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+pub enum Mid<T> {
+ Before,
+ Thing(T),
+ After,
+}
+
+#[unsafe(no_mangle)]
+pub fn mid_bool_eq_discr(a: Mid<bool>, b: Mid<bool>) -> bool {
+ // CHECK-LABEL: @mid_bool_eq_discr(
+
+ // CHECK: %[[A_REL_DISCR:.+]] = add nsw i8 %a, -2
+ // CHECK: %[[A_IS_NICHE:.+]] = icmp ult i8 %[[A_REL_DISCR]], 3
+ // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %[[A_REL_DISCR]], 1
+ // CHECK: tail call void @llvm.assume(i1 %[[A_NOT_HOLE]])
+ // CHECK: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %[[A_REL_DISCR]], i8 1
+
+ // CHECK: %[[B_REL_DISCR:.+]] = add nsw i8 %b, -2
+ // CHECK: %[[B_IS_NICHE:.+]] = icmp ult i8 %[[B_REL_DISCR]], 3
+ // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %[[B_REL_DISCR]], 1
+ // CHECK: tail call void @llvm.assume(i1 %[[B_NOT_HOLE]])
+ // CHECK: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %[[B_REL_DISCR]], i8 1
+
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+#[unsafe(no_mangle)]
+pub fn mid_ord_eq_discr(a: Mid<Ordering>, b: Mid<Ordering>) -> bool {
+ // CHECK-LABEL: @mid_ord_eq_discr(
+
+ // CHECK: %[[A_REL_DISCR:.+]] = add nsw i8 %a, -2
+ // CHECK: %[[A_IS_NICHE:.+]] = icmp ult i8 %[[A_REL_DISCR]], 3
+ // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %[[A_REL_DISCR]], 1
+ // CHECK: tail call void @llvm.assume(i1 %[[A_NOT_HOLE]])
+ // CHECK: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %[[A_REL_DISCR]], i8 1
+
+ // CHECK: %[[B_REL_DISCR:.+]] = add nsw i8 %b, -2
+ // CHECK: %[[B_IS_NICHE:.+]] = icmp ult i8 %[[B_REL_DISCR]], 3
+ // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %[[B_REL_DISCR]], 1
+ // CHECK: tail call void @llvm.assume(i1 %[[B_NOT_HOLE]])
+ // CHECK: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %[[B_REL_DISCR]], i8 1
+
+ // CHECK: %[[R:.+]] = icmp eq i8 %[[A_DISCR]], %[[B_DISCR]]
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+#[unsafe(no_mangle)]
+pub fn mid_nz32_eq_discr(a: Mid<NonZero<u32>>, b: Mid<NonZero<u32>>) -> bool {
+ // CHECK-LABEL: @mid_nz32_eq_discr(
+ // CHECK: %[[R:.+]] = icmp eq i32 %a.0, %b.0
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+#[unsafe(no_mangle)]
+pub fn mid_ac_eq_discr(a: Mid<AC>, b: Mid<AC>) -> bool {
+ // CHECK-LABEL: @mid_ac_eq_discr(
+
+ // CHECK: %[[A_REL_DISCR:.+]] = xor i8 %a, -128
+ // CHECK: %[[A_IS_NICHE:.+]] = icmp ult i8 %[[A_REL_DISCR]], 3
+ // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %a, -127
+ // CHECK: tail call void @llvm.assume(i1 %[[A_NOT_HOLE]])
+ // CHECK: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %[[A_REL_DISCR]], i8 1
+
+ // CHECK: %[[B_REL_DISCR:.+]] = xor i8 %b, -128
+ // CHECK: %[[B_IS_NICHE:.+]] = icmp ult i8 %[[B_REL_DISCR]], 3
+ // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %b, -127
+ // CHECK: tail call void @llvm.assume(i1 %[[B_NOT_HOLE]])
+ // CHECK: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %[[B_REL_DISCR]], i8 1
+
+ // CHECK: %[[R:.+]] = icmp eq i8 %[[A_DISCR]], %[[B_DISCR]]
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+// FIXME: This should be improved once our LLVM fork picks up the fix for
+// <https://github.com/llvm/llvm-project/issues/134024>
+#[unsafe(no_mangle)]
+pub fn mid_giant_eq_discr(a: Mid<Giant>, b: Mid<Giant>) -> bool {
+ // CHECK-LABEL: @mid_giant_eq_discr(
+
+ // CHECK: %[[A_REL_DISCR_WIDE:.+]] = add nsw i128 %a, -5
+ // CHECK: %[[A_REL_DISCR:.+]] = trunc nsw i128 %[[A_REL_DISCR_WIDE]] to i64
+ // CHECK: %[[A_IS_NICHE:.+]] = icmp ult i128 %[[A_REL_DISCR_WIDE]], 3
+ // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i128 %[[A_REL_DISCR_WIDE]], 1
+ // CHECK: tail call void @llvm.assume(i1 %[[A_NOT_HOLE]])
+ // CHECK: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i64 %[[A_REL_DISCR]], i64 1
+
+ // CHECK: %[[B_REL_DISCR_WIDE:.+]] = add nsw i128 %b, -5
+ // CHECK: %[[B_REL_DISCR:.+]] = trunc nsw i128 %[[B_REL_DISCR_WIDE]] to i64
+ // CHECK: %[[B_IS_NICHE:.+]] = icmp ult i128 %[[B_REL_DISCR_WIDE]], 3
+ // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i128 %[[B_REL_DISCR_WIDE]], 1
+ // CHECK: tail call void @llvm.assume(i1 %[[B_NOT_HOLE]])
+ // CHECK: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i64 %[[B_REL_DISCR]], i64 1
+
+ // CHECK: %[[R:.+]] = icmp eq i64 %[[A_DISCR]], %[[B_DISCR]]
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == discriminant_value(&b)
+}
+
+// In niche-encoded enums, testing for the untagged variant should optimize to a
+// straight-forward comparison looking for the natural range of the payload value.
+// FIXME: A bunch don't, though.
+
+#[unsafe(no_mangle)]
+pub fn mid_bool_is_thing(a: Mid<bool>) -> bool {
+ // CHECK-LABEL: @mid_bool_is_thing(
+
+ // CHECK: %[[REL_DISCR:.+]] = add nsw i8 %a, -2
+ // CHECK: %[[R:.+]] = icmp ugt i8 %[[REL_DISCR]], 2
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == 1
+}
+
+#[unsafe(no_mangle)]
+pub fn mid_ord_is_thing(a: Mid<Ordering>) -> bool {
+ // CHECK-LABEL: @mid_ord_is_thing(
+ // CHECK: %[[REL_DISCR:.+]] = add nsw i8 %a, -2
+ // CHECK: %[[R:.+]] = icmp ugt i8 %[[REL_DISCR]], 2
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == 1
+}
+
+#[unsafe(no_mangle)]
+pub fn mid_nz32_is_thing(a: Mid<NonZero<u32>>) -> bool {
+ // CHECK-LABEL: @mid_nz32_is_thing(
+ // CHECK: %[[R:.+]] = icmp eq i32 %a.0, 1
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == 1
+}
+
+#[unsafe(no_mangle)]
+pub fn mid_ac_is_thing(a: Mid<AC>) -> bool {
+ // CHECK-LABEL: @mid_ac_is_thing(
+ // CHECK: %[[R:.+]] = icmp sgt i8 %a, -1
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == 1
+}
+
+#[unsafe(no_mangle)]
+pub fn mid_giant_is_thing(a: Mid<Giant>) -> bool {
+ // CHECK-LABEL: @mid_giant_is_thing(
+ // CHECK: %[[REL_DISCR_WIDE:.+]] = add nsw i128 %a, -5
+ // CHECK: %[[REL_DISCR:.+]] = trunc nsw i128 %[[REL_DISCR_WIDE]] to i64
+ // CHECK: %[[NOT_NICHE:.+]] = icmp ugt i128 %[[REL_DISCR_WIDE]], 2
+ // CHECK: %[[IS_MID_VARIANT:.+]] = icmp eq i64 %[[REL_DISCR]], 1
+ // CHECK: %[[R:.+]] = select i1 %[[NOT_NICHE]], i1 true, i1 %[[IS_MID_VARIANT]]
+ // CHECK: ret i1 %[[R]]
+ discriminant_value(&a) == 1
+}
diff --git a/tests/codegen/enum/enum-match.rs b/tests/codegen/enum/enum-match.rs
index 9863500..9725e64 100644
--- a/tests/codegen/enum/enum-match.rs
+++ b/tests/codegen/enum/enum-match.rs
@@ -471,3 +471,310 @@ pub fn match5(e: HugeVariantIndex) -> u8 {
Possible259 => 100,
}
}
+
+// Make an enum where the niche tags wrap both as signed and as unsigned, to hit
+// the most-fallback case where there's just nothing smart to do.
+
+pub enum E10Through65 {
+ D10 = 10,
+ D11 = 11,
+ D12 = 12,
+ D13 = 13,
+ D14 = 14,
+ D15 = 15,
+ D16 = 16,
+ D17 = 17,
+ D18 = 18,
+ D19 = 19,
+ D20 = 20,
+ D21 = 21,
+ D22 = 22,
+ D23 = 23,
+ D24 = 24,
+ D25 = 25,
+ D26 = 26,
+ D27 = 27,
+ D28 = 28,
+ D29 = 29,
+ D30 = 30,
+ D31 = 31,
+ D32 = 32,
+ D33 = 33,
+ D34 = 34,
+ D35 = 35,
+ D36 = 36,
+ D37 = 37,
+ D38 = 38,
+ D39 = 39,
+ D40 = 40,
+ D41 = 41,
+ D42 = 42,
+ D43 = 43,
+ D44 = 44,
+ D45 = 45,
+ D46 = 46,
+ D47 = 47,
+ D48 = 48,
+ D49 = 49,
+ D50 = 50,
+ D51 = 51,
+ D52 = 52,
+ D53 = 53,
+ D54 = 54,
+ D55 = 55,
+ D56 = 56,
+ D57 = 57,
+ D58 = 58,
+ D59 = 59,
+ D60 = 60,
+ D61 = 61,
+ D62 = 62,
+ D63 = 63,
+ D64 = 64,
+ D65 = 65,
+}
+
+pub enum Tricky {
+ Untagged(E10Through65),
+ V001,
+ V002,
+ V003,
+ V004,
+ V005,
+ V006,
+ V007,
+ V008,
+ V009,
+ V010,
+ V011,
+ V012,
+ V013,
+ V014,
+ V015,
+ V016,
+ V017,
+ V018,
+ V019,
+ V020,
+ V021,
+ V022,
+ V023,
+ V024,
+ V025,
+ V026,
+ V027,
+ V028,
+ V029,
+ V030,
+ V031,
+ V032,
+ V033,
+ V034,
+ V035,
+ V036,
+ V037,
+ V038,
+ V039,
+ V040,
+ V041,
+ V042,
+ V043,
+ V044,
+ V045,
+ V046,
+ V047,
+ V048,
+ V049,
+ V050,
+ V051,
+ V052,
+ V053,
+ V054,
+ V055,
+ V056,
+ V057,
+ V058,
+ V059,
+ V060,
+ V061,
+ V062,
+ V063,
+ V064,
+ V065,
+ V066,
+ V067,
+ V068,
+ V069,
+ V070,
+ V071,
+ V072,
+ V073,
+ V074,
+ V075,
+ V076,
+ V077,
+ V078,
+ V079,
+ V080,
+ V081,
+ V082,
+ V083,
+ V084,
+ V085,
+ V086,
+ V087,
+ V088,
+ V089,
+ V090,
+ V091,
+ V092,
+ V093,
+ V094,
+ V095,
+ V096,
+ V097,
+ V098,
+ V099,
+ V100,
+ V101,
+ V102,
+ V103,
+ V104,
+ V105,
+ V106,
+ V107,
+ V108,
+ V109,
+ V110,
+ V111,
+ V112,
+ V113,
+ V114,
+ V115,
+ V116,
+ V117,
+ V118,
+ V119,
+ V120,
+ V121,
+ V122,
+ V123,
+ V124,
+ V125,
+ V126,
+ V127,
+ V128,
+ V129,
+ V130,
+ V131,
+ V132,
+ V133,
+ V134,
+ V135,
+ V136,
+ V137,
+ V138,
+ V139,
+ V140,
+ V141,
+ V142,
+ V143,
+ V144,
+ V145,
+ V146,
+ V147,
+ V148,
+ V149,
+ V150,
+ V151,
+ V152,
+ V153,
+ V154,
+ V155,
+ V156,
+ V157,
+ V158,
+ V159,
+ V160,
+ V161,
+ V162,
+ V163,
+ V164,
+ V165,
+ V166,
+ V167,
+ V168,
+ V169,
+ V170,
+ V171,
+ V172,
+ V173,
+ V174,
+ V175,
+ V176,
+ V177,
+ V178,
+ V179,
+ V180,
+ V181,
+ V182,
+ V183,
+ V184,
+ V185,
+ V186,
+ V187,
+ V188,
+ V189,
+ V190,
+ V191,
+ V192,
+ V193,
+ V194,
+ V195,
+ V196,
+ V197,
+ V198,
+ V199,
+ V200,
+}
+
+const _: () = assert!(std::intrinsics::discriminant_value(&Tricky::V100) == 100);
+
+// CHECK-LABEL: define noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @discriminant6(i8 noundef %e)
+// CHECK-NEXT: start:
+// CHECK-NEXT: %[[REL_VAR:.+]] = add i8 %e, -66
+// CHECK-NEXT: %[[IS_NICHE:.+]] = icmp ult i8 %[[REL_VAR]], -56
+// CHECK-NEXT: %[[TAGGED_DISCR:.+]] = add i8 %e, -65
+// CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i8 %[[TAGGED_DISCR]], i8 0
+// CHECK-NEXT: ret i8 %[[DISCR]]
+#[no_mangle]
+pub fn discriminant6(e: Tricky) -> u8 {
+ std::intrinsics::discriminant_value(&e) as _
+}
+
+// Case from <https://github.com/rust-lang/rust/issues/104519>,
+// where sign-extension is important.
+
+pub enum OpenResult {
+ Ok(()),
+ Err(()),
+ TransportErr(TransportErr),
+}
+
+#[repr(i32)]
+pub enum TransportErr {
+ UnknownMethod = -2,
+}
+
+#[no_mangle]
+pub fn match7(result: OpenResult) -> u8 {
+ // CHECK-LABEL: define noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match7(i32{{.+}}%result)
+ // CHECK-NEXT: start:
+ // CHECK-NEXT: %[[NOT_OK:.+]] = icmp ne i32 %result, -1
+ // CHECK-NEXT: %[[RET:.+]] = zext i1 %[[NOT_OK]] to i8
+ // CHECK-NEXT: ret i8 %[[RET]]
+ match result {
+ OpenResult::Ok(()) => 0,
+ _ => 1,
+ }
+}