1 //! Adapted from https://github.com/rust-lang/rust/blob/d760df5aea483aae041c9a241e7acacf48f75035/src/librustc_codegen_ssa/mir/place.rs
5 pub fn codegen_set_discriminant<'tcx>(
6 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
8 variant_index: VariantIdx,
10 let layout = place.layout();
11 if layout.for_variant(fx, variant_index).abi.is_uninhabited() {
14 match layout.variants {
15 layout::Variants::Single { index } => {
16 assert_eq!(index, variant_index);
18 layout::Variants::Multiple {
21 discr_kind: layout::DiscriminantKind::Tag,
24 let ptr = place.place_field(fx, mir::Field::new(discr_index));
27 .discriminant_for_variant(fx.tcx, variant_index)
30 let discr = CValue::const_val(fx, ptr.layout().ty, to);
31 ptr.write_cvalue(fx, discr);
33 layout::Variants::Multiple {
37 layout::DiscriminantKind::Niche {
44 if variant_index != dataful_variant {
45 let niche = place.place_field(fx, mir::Field::new(discr_index));
46 let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
47 let niche_value = u128::from(niche_value).wrapping_add(niche_start);
48 let niche_llval = CValue::const_val(fx, niche.layout().ty, niche_value);
49 niche.write_cvalue(fx, niche_llval);
55 pub fn codegen_get_discriminant<'tcx>(
56 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
58 dest_layout: TyLayout<'tcx>,
60 let layout = value.layout();
62 if layout.abi == layout::Abi::Uninhabited {
63 return trap_unreachable_ret_value(
66 "[panic] Tried to get discriminant for uninhabited type.",
70 let (discr_scalar, discr_index, discr_kind) = match &layout.variants {
71 layout::Variants::Single { index } => {
72 let discr_val = layout
74 .discriminant_for_variant(fx.tcx, *index)
75 .map_or(u128::from(index.as_u32()), |discr| discr.val);
76 return CValue::const_val(fx, dest_layout.ty, discr_val);
78 layout::Variants::Multiple {
83 } => (discr, *discr_index, discr_kind),
86 let cast_to = fx.clif_type(dest_layout.ty).unwrap();
88 // Read the tag/niche-encoded discriminant from memory.
89 let encoded_discr = value.value_field(fx, mir::Field::new(discr_index));
90 let encoded_discr = encoded_discr.load_scalar(fx);
92 // Decode the discriminant (specifically if it's niche-encoded).
94 layout::DiscriminantKind::Tag => {
95 let signed = match discr_scalar.value {
96 layout::Int(_, signed) => signed,
99 let val = clif_intcast(fx, encoded_discr, cast_to, signed);
100 return CValue::by_val(val, dest_layout);
102 layout::DiscriminantKind::Niche {
107 // Rebase from niche values to discriminants, and check
108 // whether the result is in range for the niche variants.
110 // We first compute the "relative discriminant" (wrt `niche_variants`),
111 // that is, if `n = niche_variants.end() - niche_variants.start()`,
112 // we remap `niche_start..=niche_start + n` (which may wrap around)
113 // to (non-wrap-around) `0..=n`, to be able to check whether the
114 // discriminant corresponds to a niche variant with one comparison.
115 // We also can't go directly to the (variant index) discriminant
116 // and check that it is in the range `niche_variants`, because
117 // that might not fit in the same type, on top of needing an extra
118 // comparison (see also the comment on `let niche_discr`).
119 let relative_discr = if niche_start == 0 {
122 // FIXME handle niche_start > i64::max_value()
125 .iadd_imm(encoded_discr, -i64::try_from(niche_start).unwrap())
127 let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
131 IntCC::UnsignedLessThanOrEqual,
133 i128::from(relative_max),
137 // NOTE(eddyb) this addition needs to be performed on the final
138 // type, in case the niche itself can't represent all variant
139 // indices (e.g. `u8` niche with more than `256` variants,
140 // but enough uninhabited variants so that the remaining variants
141 // fit in the niche).
142 // In other words, `niche_variants.end - niche_variants.start`
143 // is representable in the niche, but `niche_variants.end`
144 // might not be, in extreme cases.
146 let relative_discr = if relative_max == 0 {
147 // HACK(eddyb) since we have only one niche, we know which
148 // one it is, and we can avoid having a dynamic value here.
149 fx.bcx.ins().iconst(cast_to, 0)
151 clif_intcast(fx, relative_discr, cast_to, false)
155 .iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32()))
158 let dataful_variant = fx
161 .iconst(cast_to, i64::from(dataful_variant.as_u32()));
162 let discr = fx.bcx.ins().select(is_niche, niche_discr, dataful_variant);
163 CValue::by_val(discr, dest_layout)