]>
Commit | Line | Data |
---|---|---|
29967ef6 XL |
1 | //! Handling of enum discriminants |
2 | //! | |
9c376795 FG |
3 | //! Adapted from <https://github.com/rust-lang/rust/blob/31c0645b9d2539f47eecb096142474b29dc542f7/compiler/rustc_codegen_ssa/src/mir/place.rs> |
4 | //! (<https://github.com/rust-lang/rust/pull/104535>) | |
29967ef6 XL |
5 | |
6 | use rustc_target::abi::{Int, TagEncoding, Variants}; | |
7 | ||
8 | use crate::prelude::*; | |
9 | ||
10 | pub(crate) fn codegen_set_discriminant<'tcx>( | |
6a06907d | 11 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
29967ef6 XL |
12 | place: CPlace<'tcx>, |
13 | variant_index: VariantIdx, | |
14 | ) { | |
15 | let layout = place.layout(); | |
16 | if layout.for_variant(fx, variant_index).abi.is_uninhabited() { | |
17 | return; | |
18 | } | |
19 | match layout.variants { | |
20 | Variants::Single { index } => { | |
21 | assert_eq!(index, variant_index); | |
22 | } | |
23 | Variants::Multiple { | |
24 | tag: _, | |
25 | tag_field, | |
26 | tag_encoding: TagEncoding::Direct, | |
27 | variants: _, | |
28 | } => { | |
353b0b11 | 29 | let ptr = place.place_field(fx, FieldIdx::new(tag_field)); |
6a06907d | 30 | let to = layout.ty.discriminant_for_variant(fx.tcx, variant_index).unwrap().val; |
fc512014 XL |
31 | let to = if ptr.layout().abi.is_signed() { |
32 | ty::ScalarInt::try_from_int( | |
33 | ptr.layout().size.sign_extend(to) as i128, | |
34 | ptr.layout().size, | |
35 | ) | |
36 | .unwrap() | |
37 | } else { | |
38 | ty::ScalarInt::try_from_uint(to, ptr.layout().size).unwrap() | |
39 | }; | |
29967ef6 XL |
40 | let discr = CValue::const_val(fx, ptr.layout(), to); |
41 | ptr.write_cvalue(fx, discr); | |
42 | } | |
43 | Variants::Multiple { | |
44 | tag: _, | |
45 | tag_field, | |
f2b60f7d | 46 | tag_encoding: TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, |
29967ef6 XL |
47 | variants: _, |
48 | } => { | |
f2b60f7d | 49 | if variant_index != untagged_variant { |
353b0b11 | 50 | let niche = place.place_field(fx, FieldIdx::new(tag_field)); |
9c376795 | 51 | let niche_type = fx.clif_type(niche.layout().ty).unwrap(); |
29967ef6 | 52 | let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); |
9c376795 FG |
53 | let niche_value = (niche_value as u128).wrapping_add(niche_start); |
54 | let niche_value = match niche_type { | |
55 | types::I128 => { | |
56 | let lsb = fx.bcx.ins().iconst(types::I64, niche_value as u64 as i64); | |
57 | let msb = | |
58 | fx.bcx.ins().iconst(types::I64, (niche_value >> 64) as u64 as i64); | |
59 | fx.bcx.ins().iconcat(lsb, msb) | |
60 | } | |
61 | ty => fx.bcx.ins().iconst(ty, niche_value as i64), | |
62 | }; | |
63 | let niche_llval = CValue::by_val(niche_value, niche.layout()); | |
29967ef6 XL |
64 | niche.write_cvalue(fx, niche_llval); |
65 | } | |
66 | } | |
67 | } | |
68 | } | |
69 | ||
70 | pub(crate) fn codegen_get_discriminant<'tcx>( | |
6a06907d | 71 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
f2b60f7d | 72 | dest: CPlace<'tcx>, |
29967ef6 XL |
73 | value: CValue<'tcx>, |
74 | dest_layout: TyAndLayout<'tcx>, | |
f2b60f7d | 75 | ) { |
29967ef6 XL |
76 | let layout = value.layout(); |
77 | ||
f2b60f7d FG |
78 | if layout.abi.is_uninhabited() { |
79 | return; | |
29967ef6 XL |
80 | } |
81 | ||
82 | let (tag_scalar, tag_field, tag_encoding) = match &layout.variants { | |
83 | Variants::Single { index } => { | |
84 | let discr_val = layout | |
85 | .ty | |
86 | .discriminant_for_variant(fx.tcx, *index) | |
87 | .map_or(u128::from(index.as_u32()), |discr| discr.val); | |
fc512014 XL |
88 | let discr_val = if dest_layout.abi.is_signed() { |
89 | ty::ScalarInt::try_from_int( | |
90 | dest_layout.size.sign_extend(discr_val) as i128, | |
91 | dest_layout.size, | |
92 | ) | |
93 | .unwrap() | |
94 | } else { | |
95 | ty::ScalarInt::try_from_uint(discr_val, dest_layout.size).unwrap() | |
96 | }; | |
f2b60f7d FG |
97 | let res = CValue::const_val(fx, dest_layout, discr_val); |
98 | dest.write_cvalue(fx, res); | |
99 | return; | |
29967ef6 | 100 | } |
6a06907d XL |
101 | Variants::Multiple { tag, tag_field, tag_encoding, variants: _ } => { |
102 | (tag, *tag_field, tag_encoding) | |
103 | } | |
29967ef6 XL |
104 | }; |
105 | ||
106 | let cast_to = fx.clif_type(dest_layout.ty).unwrap(); | |
107 | ||
108 | // Read the tag/niche-encoded discriminant from memory. | |
353b0b11 | 109 | let tag = value.value_field(fx, FieldIdx::new(tag_field)); |
29967ef6 XL |
110 | let tag = tag.load_scalar(fx); |
111 | ||
112 | // Decode the discriminant (specifically if it's niche-encoded). | |
113 | match *tag_encoding { | |
114 | TagEncoding::Direct => { | |
04454e1e | 115 | let signed = match tag_scalar.primitive() { |
29967ef6 XL |
116 | Int(_, signed) => signed, |
117 | _ => false, | |
118 | }; | |
119 | let val = clif_intcast(fx, tag, cast_to, signed); | |
f2b60f7d FG |
120 | let res = CValue::by_val(val, dest_layout); |
121 | dest.write_cvalue(fx, res); | |
29967ef6 | 122 | } |
f2b60f7d | 123 | TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { |
9c376795 | 124 | let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); |
9c376795 FG |
125 | |
126 | // We have a subrange `niche_start..=niche_end` inside `range`. | |
127 | // If the value of the tag is inside this subrange, it's a | |
128 | // "niche value", an increment of the discriminant. Otherwise it | |
129 | // indicates the untagged variant. | |
130 | // A general algorithm to extract the discriminant from the tag | |
131 | // is: | |
132 | // relative_tag = tag - niche_start | |
133 | // is_niche = relative_tag <= (ule) relative_max | |
134 | // discr = if is_niche { | |
135 | // cast(relative_tag) + niche_variants.start() | |
136 | // } else { | |
137 | // untagged_variant | |
138 | // } | |
139 | // However, we will likely be able to emit simpler code. | |
140 | ||
9c376795 FG |
141 | let (is_niche, tagged_discr, delta) = if relative_max == 0 { |
142 | // Best case scenario: only one tagged variant. This will | |
143 | // likely become just a comparison and a jump. | |
144 | // The algorithm is: | |
145 | // is_niche = tag == niche_start | |
146 | // discr = if is_niche { | |
147 | // niche_start | |
148 | // } else { | |
149 | // untagged_variant | |
150 | // } | |
151 | let is_niche = codegen_icmp_imm(fx, IntCC::Equal, tag, niche_start as i128); | |
152 | let tagged_discr = | |
153 | fx.bcx.ins().iconst(cast_to, niche_variants.start().as_u32() as i64); | |
154 | (is_niche, tagged_discr, 0) | |
9c376795 FG |
155 | } else { |
156 | // The special cases don't apply, so we'll have to go with | |
157 | // the general algorithm. | |
04454e1e FG |
158 | let niche_start = match fx.bcx.func.dfg.value_type(tag) { |
159 | types::I128 => { | |
160 | let lsb = fx.bcx.ins().iconst(types::I64, niche_start as u64 as i64); | |
161 | let msb = | |
162 | fx.bcx.ins().iconst(types::I64, (niche_start >> 64) as u64 as i64); | |
163 | fx.bcx.ins().iconcat(lsb, msb) | |
164 | } | |
165 | ty => fx.bcx.ins().iconst(ty, niche_start as i64), | |
166 | }; | |
9c376795 FG |
167 | let relative_discr = fx.bcx.ins().isub(tag, niche_start); |
168 | let cast_tag = clif_intcast(fx, relative_discr, cast_to, false); | |
169 | let is_niche = crate::common::codegen_icmp_imm( | |
29967ef6 XL |
170 | fx, |
171 | IntCC::UnsignedLessThanOrEqual, | |
172 | relative_discr, | |
173 | i128::from(relative_max), | |
9c376795 FG |
174 | ); |
175 | (is_niche, cast_tag, niche_variants.start().as_u32() as u128) | |
29967ef6 XL |
176 | }; |
177 | ||
9c376795 FG |
178 | let tagged_discr = if delta == 0 { |
179 | tagged_discr | |
180 | } else { | |
181 | let delta = match cast_to { | |
182 | types::I128 => { | |
183 | let lsb = fx.bcx.ins().iconst(types::I64, delta as u64 as i64); | |
184 | let msb = fx.bcx.ins().iconst(types::I64, (delta >> 64) as u64 as i64); | |
185 | fx.bcx.ins().iconcat(lsb, msb) | |
186 | } | |
187 | ty => fx.bcx.ins().iconst(ty, delta as i64), | |
29967ef6 | 188 | }; |
9c376795 | 189 | fx.bcx.ins().iadd(tagged_discr, delta) |
29967ef6 XL |
190 | }; |
191 | ||
9c376795 FG |
192 | let untagged_variant = if cast_to == types::I128 { |
193 | let zero = fx.bcx.ins().iconst(types::I64, 0); | |
194 | let untagged_variant = | |
195 | fx.bcx.ins().iconst(types::I64, i64::from(untagged_variant.as_u32())); | |
196 | fx.bcx.ins().iconcat(untagged_variant, zero) | |
197 | } else { | |
198 | fx.bcx.ins().iconst(cast_to, i64::from(untagged_variant.as_u32())) | |
199 | }; | |
200 | let discr = fx.bcx.ins().select(is_niche, tagged_discr, untagged_variant); | |
f2b60f7d FG |
201 | let res = CValue::by_val(discr, dest_layout); |
202 | dest.write_cvalue(fx, res); | |
29967ef6 XL |
203 | } |
204 | } | |
205 | } |