]> git.lizzy.rs Git - rust.git/blob - src/discriminant.rs
FunctionCx: WIP: Replace .tcx with .codegen_cx.tcx
[rust.git] / src / discriminant.rs
1 //! Adapted from https://github.com/rust-lang/rust/blob/d760df5aea483aae041c9a241e7acacf48f75035/src/librustc_codegen_ssa/mir/place.rs
2
3 use rustc_target::abi::{TagEncoding, Int, Variants};
4
5 use crate::prelude::*;
6
7 pub(crate) fn codegen_set_discriminant<'tcx>(
8     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
9     place: CPlace<'tcx>,
10     variant_index: VariantIdx,
11 ) {
12     let layout = place.layout();
13     if layout.for_variant(fx, variant_index).abi.is_uninhabited() {
14         return;
15     }
16     match layout.variants {
17         Variants::Single { index } => {
18             assert_eq!(index, variant_index);
19         }
20         Variants::Multiple {
21             tag: _,
22             tag_field,
23             tag_encoding: TagEncoding::Direct,
24             variants: _,
25         } => {
26             let ptr = place.place_field(fx, mir::Field::new(tag_field));
27             let to = layout
28                 .ty
29                 .discriminant_for_variant(fxcodegen_cx.tcx, variant_index)
30                 .unwrap()
31                 .val;
32             let discr = CValue::const_val(fx, ptr.layout(), to);
33             ptr.write_cvalue(fx, discr);
34         }
35         Variants::Multiple {
36             tag: _,
37             tag_field,
38             tag_encoding:
39                 TagEncoding::Niche {
40                     dataful_variant,
41                     ref niche_variants,
42                     niche_start,
43                 },
44             variants: _,
45         } => {
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);
52             }
53         }
54     }
55 }
56
57 pub(crate) fn codegen_get_discriminant<'tcx>(
58     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
59     value: CValue<'tcx>,
60     dest_layout: TyAndLayout<'tcx>,
61 ) -> CValue<'tcx> {
62     let layout = value.layout();
63
64     if layout.abi == Abi::Uninhabited {
65         return trap_unreachable_ret_value(
66             fx,
67             dest_layout,
68             "[panic] Tried to get discriminant for uninhabited type.",
69         );
70     }
71
72     let (tag_scalar, tag_field, tag_encoding) = match &layout.variants {
73         Variants::Single { index } => {
74             let discr_val = layout
75                 .ty
76                 .discriminant_for_variant(fxcodegen_cx.tcx, *index)
77                 .map_or(u128::from(index.as_u32()), |discr| discr.val);
78             return CValue::const_val(fx, dest_layout, discr_val);
79         }
80         Variants::Multiple {
81             tag,
82             tag_field,
83             tag_encoding,
84             variants: _,
85         } => (tag, *tag_field, tag_encoding),
86     };
87
88     let cast_to = fx.clif_type(dest_layout.ty).unwrap();
89
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);
93
94     // Decode the discriminant (specifically if it's niche-encoded).
95     match *tag_encoding {
96         TagEncoding::Direct => {
97             let signed = match tag_scalar.value {
98                 Int(_, signed) => signed,
99                 _ => false,
100             };
101             let val = clif_intcast(fx, tag, cast_to, signed);
102             CValue::by_val(val, dest_layout)
103         }
104         TagEncoding::Niche {
105             dataful_variant,
106             ref niche_variants,
107             niche_start,
108         } => {
109             // Rebase from niche values to discriminants, and check
110             // whether the result is in range for the niche variants.
111
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 {
122                 tag
123             } else {
124                 // FIXME handle niche_start > i64::MAX
125                 fx.bcx
126                     .ins()
127                     .iadd_imm(tag, -i64::try_from(niche_start).unwrap())
128             };
129             let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
130             let is_niche = {
131                 codegen_icmp_imm(
132                     fx,
133                     IntCC::UnsignedLessThanOrEqual,
134                     relative_discr,
135                     i128::from(relative_max),
136                 )
137             };
138
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.
147             let niche_discr = {
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)
152                 } else {
153                     clif_intcast(fx, relative_discr, cast_to, false)
154                 };
155                 fx.bcx
156                     .ins()
157                     .iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32()))
158             };
159
160             let dataful_variant = fx
161                 .bcx
162                 .ins()
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)
166         }
167     }
168 }