]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_cranelift/src/discriminant.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_codegen_cranelift / src / discriminant.rs
CommitLineData
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
6use rustc_target::abi::{Int, TagEncoding, Variants};
7
8use crate::prelude::*;
9
10pub(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
70pub(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}