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 {
36 discr_kind: layout::DiscriminantKind::Niche {
43 if variant_index != dataful_variant {
44 let niche = place.place_field(fx, mir::Field::new(discr_index));
45 let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
46 let niche_value = u128::from(niche_value).wrapping_add(niche_start);
47 let niche_llval = CValue::const_val(fx, niche.layout().ty, niche_value);
48 niche.write_cvalue(fx, niche_llval);
54 pub fn codegen_get_discriminant<'tcx>(
55 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
57 dest_layout: TyLayout<'tcx>,
59 let layout = value.layout();
61 if layout.abi == layout::Abi::Uninhabited {
62 return trap_unreachable_ret_value(fx, dest_layout, "[panic] Tried to get discriminant for uninhabited type.");
65 let (discr_scalar, discr_index, discr_kind) = match &layout.variants {
66 layout::Variants::Single { index } => {
67 let discr_val = layout
69 .discriminant_for_variant(fx.tcx, *index)
70 .map_or(u128::from(index.as_u32()), |discr| discr.val);
71 return CValue::const_val(fx, dest_layout.ty, discr_val);
73 layout::Variants::Multiple { discr, discr_index, discr_kind, variants: _ } => {
74 (discr, *discr_index, discr_kind)
78 let cast_to = fx.clif_type(dest_layout.ty).unwrap();
80 // Read the tag/niche-encoded discriminant from memory.
81 let encoded_discr = value.value_field(fx, mir::Field::new(discr_index));
82 let encoded_discr = encoded_discr.load_scalar(fx);
84 // Decode the discriminant (specifically if it's niche-encoded).
86 layout::DiscriminantKind::Tag => {
87 let signed = match discr_scalar.value {
88 layout::Int(_, signed) => signed,
91 let val = clif_intcast(fx, encoded_discr, cast_to, signed);
92 return CValue::by_val(val, dest_layout);
94 layout::DiscriminantKind::Niche {
99 // Rebase from niche values to discriminants, and check
100 // whether the result is in range for the niche variants.
102 // We first compute the "relative discriminant" (wrt `niche_variants`),
103 // that is, if `n = niche_variants.end() - niche_variants.start()`,
104 // we remap `niche_start..=niche_start + n` (which may wrap around)
105 // to (non-wrap-around) `0..=n`, to be able to check whether the
106 // discriminant corresponds to a niche variant with one comparison.
107 // We also can't go directly to the (variant index) discriminant
108 // and check that it is in the range `niche_variants`, because
109 // that might not fit in the same type, on top of needing an extra
110 // comparison (see also the comment on `let niche_discr`).
111 let relative_discr = if niche_start == 0 {
114 // FIXME handle niche_start > i64::max_value()
115 fx.bcx.ins().iadd_imm(encoded_discr, -i64::try_from(niche_start).unwrap())
117 let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
119 codegen_icmp_imm(fx, IntCC::UnsignedLessThanOrEqual, relative_discr, i128::from(relative_max))
122 // NOTE(eddyb) this addition needs to be performed on the final
123 // type, in case the niche itself can't represent all variant
124 // indices (e.g. `u8` niche with more than `256` variants,
125 // but enough uninhabited variants so that the remaining variants
126 // fit in the niche).
127 // In other words, `niche_variants.end - niche_variants.start`
128 // is representable in the niche, but `niche_variants.end`
129 // might not be, in extreme cases.
131 let relative_discr = if relative_max == 0 {
132 // HACK(eddyb) since we have only one niche, we know which
133 // one it is, and we can avoid having a dynamic value here.
134 fx.bcx.ins().iconst(cast_to, 0)
136 clif_intcast(fx, relative_discr, cast_to, false)
138 fx.bcx.ins().iadd_imm(
140 i64::from(niche_variants.start().as_u32()),
144 let dataful_variant = fx.bcx.ins().iconst(cast_to, i64::from(dataful_variant.as_u32()));
145 let discr = fx.bcx.ins().select(
150 CValue::by_val(discr, dest_layout)