1 //! Adapted from https://github.com/rust-lang/rust/blob/d760df5aea483aae041c9a241e7acacf48f75035/src/librustc_codegen_ssa/mir/place.rs
3 use rustc_target::abi::{TagEncoding, Int, Variants};
7 pub(crate) fn codegen_set_discriminant<'tcx>(
8 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
10 variant_index: VariantIdx,
12 let layout = place.layout();
13 if layout.for_variant(fx, variant_index).abi.is_uninhabited() {
16 match layout.variants {
17 Variants::Single { index } => {
18 assert_eq!(index, variant_index);
23 tag_encoding: TagEncoding::Direct,
26 let ptr = place.place_field(fx, mir::Field::new(tag_field));
29 .discriminant_for_variant(fx.tcx, variant_index)
32 let discr = CValue::const_val(fx, ptr.layout(), to);
33 ptr.write_cvalue(fx, discr);
46 if variant_index != dataful_variant {
47 let niche = place.place_field(fx, mir::Field::new(tag_field));
48 let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
49 let niche_value = u128::from(niche_value).wrapping_add(niche_start);
50 let niche_llval = CValue::const_val(fx, niche.layout(), niche_value);
51 niche.write_cvalue(fx, niche_llval);
57 pub(crate) fn codegen_get_discriminant<'tcx>(
58 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
60 dest_layout: TyAndLayout<'tcx>,
62 let layout = value.layout();
64 if layout.abi == Abi::Uninhabited {
65 return trap_unreachable_ret_value(
68 "[panic] Tried to get discriminant for uninhabited type.",
72 let (tag_scalar, tag_field, tag_encoding) = match &layout.variants {
73 Variants::Single { index } => {
74 let discr_val = layout
76 .discriminant_for_variant(fx.tcx, *index)
77 .map_or(u128::from(index.as_u32()), |discr| discr.val);
78 return CValue::const_val(fx, dest_layout, discr_val);
85 } => (tag, *tag_field, tag_encoding),
88 let cast_to = fx.clif_type(dest_layout.ty).unwrap();
90 // Read the tag/niche-encoded discriminant from memory.
91 let tag = value.value_field(fx, mir::Field::new(tag_field));
92 let tag = tag.load_scalar(fx);
94 // Decode the discriminant (specifically if it's niche-encoded).
96 TagEncoding::Direct => {
97 let signed = match tag_scalar.value {
98 Int(_, signed) => signed,
101 let val = clif_intcast(fx, tag, cast_to, signed);
102 CValue::by_val(val, dest_layout)
109 // Rebase from niche values to discriminants, and check
110 // whether the result is in range for the niche variants.
112 // We first compute the "relative discriminant" (wrt `niche_variants`),
113 // that is, if `n = niche_variants.end() - niche_variants.start()`,
114 // we remap `niche_start..=niche_start + n` (which may wrap around)
115 // to (non-wrap-around) `0..=n`, to be able to check whether the
116 // discriminant corresponds to a niche variant with one comparison.
117 // We also can't go directly to the (variant index) discriminant
118 // and check that it is in the range `niche_variants`, because
119 // that might not fit in the same type, on top of needing an extra
120 // comparison (see also the comment on `let niche_discr`).
121 let relative_discr = if niche_start == 0 {
124 // FIXME handle niche_start > i64::MAX
127 .iadd_imm(tag, -i64::try_from(niche_start).unwrap())
129 let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
133 IntCC::UnsignedLessThanOrEqual,
135 i128::from(relative_max),
139 // NOTE(eddyb) this addition needs to be performed on the final
140 // type, in case the niche itself can't represent all variant
141 // indices (e.g. `u8` niche with more than `256` variants,
142 // but enough uninhabited variants so that the remaining variants
143 // fit in the niche).
144 // In other words, `niche_variants.end - niche_variants.start`
145 // is representable in the niche, but `niche_variants.end`
146 // might not be, in extreme cases.
148 let relative_discr = if relative_max == 0 {
149 // HACK(eddyb) since we have only one niche, we know which
150 // one it is, and we can avoid having a dynamic value here.
151 fx.bcx.ins().iconst(cast_to, 0)
153 clif_intcast(fx, relative_discr, cast_to, false)
157 .iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32()))
160 let dataful_variant = fx
163 .iconst(cast_to, i64::from(dataful_variant.as_u32()));
164 let discr = fx.bcx.ins().select(is_niche, niche_discr, dataful_variant);
165 CValue::by_val(discr, dest_layout)