@@ -52,10 +52,20 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
52
52
variants : _,
53
53
} => {
54
54
if variant_index != untagged_variant {
55
+ let discr_len = niche_variants. end ( ) . index ( ) - niche_variants. start ( ) . index ( ) + 1 ;
56
+ let adj_idx = variant_index. index ( ) - niche_variants. start ( ) . index ( ) ;
57
+
55
58
let niche = place. place_field ( fx, FieldIdx :: new ( tag_field) ) ;
56
59
let niche_type = fx. clif_type ( niche. layout ( ) . ty ) . unwrap ( ) ;
57
- let niche_value = variant_index. as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ;
58
- let niche_value = ( niche_value as u128 ) . wrapping_add ( niche_start) ;
60
+
61
+ let discr = if niche_variants. contains ( & untagged_variant) {
62
+ let adj_untagged_idx =
63
+ untagged_variant. index ( ) - niche_variants. start ( ) . index ( ) ;
64
+ ( adj_idx + discr_len - adj_untagged_idx) % discr_len - 1
65
+ } else {
66
+ adj_idx
67
+ } ;
68
+ let niche_value = ( discr as u128 ) . wrapping_add ( niche_start) ;
59
69
let niche_value = match niche_type {
60
70
types:: I128 => {
61
71
let lsb = fx. bcx . ins ( ) . iconst ( types:: I64 , niche_value as u64 as i64 ) ;
@@ -131,72 +141,91 @@ pub(crate) fn codegen_get_discriminant<'tcx>(
131
141
dest. write_cvalue ( fx, res) ;
132
142
}
133
143
TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } => {
134
- let relative_max = niche_variants. end ( ) . as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ;
135
-
136
- // We have a subrange `niche_start..=niche_end` inside `range`.
137
- // If the value of the tag is inside this subrange, it's a
138
- // "niche value", an increment of the discriminant. Otherwise it
139
- // indicates the untagged variant.
140
- // A general algorithm to extract the discriminant from the tag
141
- // is:
142
- // relative_tag = tag - niche_start
143
- // is_niche = relative_tag <= (ule) relative_max
144
- // discr = if is_niche {
145
- // cast(relative_tag) + niche_variants.start()
146
- // } else {
147
- // untagged_variant
148
- // }
149
- // However, we will likely be able to emit simpler code.
150
-
151
- let ( is_niche, tagged_discr, delta) = if relative_max == 0 {
152
- // Best case scenario: only one tagged variant. This will
153
- // likely become just a comparison and a jump.
154
- // The algorithm is:
155
- // is_niche = tag == niche_start
156
- // discr = if is_niche {
157
- // niche_start
158
- // } else {
159
- // untagged_variant
160
- // }
144
+ // See the algorithm explanation in the definition of `TagEncoding::Niche`.
145
+ let discr_len = niche_variants. end ( ) . index ( ) - niche_variants. start ( ) . index ( ) + 1 ;
146
+
147
+ let niche_start_value = match fx. bcx . func . dfg . value_type ( tag) {
148
+ types:: I128 => {
149
+ let lsb = fx. bcx . ins ( ) . iconst ( types:: I64 , niche_start as u64 as i64 ) ;
150
+ let msb = fx. bcx . ins ( ) . iconst ( types:: I64 , ( niche_start >> 64 ) as u64 as i64 ) ;
151
+ fx. bcx . ins ( ) . iconcat ( lsb, msb)
152
+ }
153
+ ty => fx. bcx . ins ( ) . iconst ( ty, niche_start as i64 ) ,
154
+ } ;
155
+
156
+ let ( is_niche, tagged_discr) = if discr_len == 1 {
157
+ // Special case where we only have a single tagged variant.
158
+ // The untagged variant can't be contained in niche_variant's range in this case.
159
+ // Thus the discriminant of the only tagged variant is 0 and its variant index
160
+ // is the start of niche_variants.
161
161
let is_niche = codegen_icmp_imm ( fx, IntCC :: Equal , tag, niche_start as i128 ) ;
162
162
let tagged_discr =
163
163
fx. bcx . ins ( ) . iconst ( cast_to, niche_variants. start ( ) . as_u32 ( ) as i64 ) ;
164
- ( is_niche, tagged_discr, 0 )
164
+ ( is_niche, tagged_discr)
165
165
} else {
166
- // The special cases don't apply, so we'll have to go with
167
- // the general algorithm.
168
- let niche_start = match fx. bcx . func . dfg . value_type ( tag) {
169
- types:: I128 => {
170
- let lsb = fx. bcx . ins ( ) . iconst ( types:: I64 , niche_start as u64 as i64 ) ;
171
- let msb =
172
- fx. bcx . ins ( ) . iconst ( types:: I64 , ( niche_start >> 64 ) as u64 as i64 ) ;
173
- fx. bcx . ins ( ) . iconcat ( lsb, msb)
174
- }
175
- ty => fx. bcx . ins ( ) . iconst ( ty, niche_start as i64 ) ,
176
- } ;
177
- let relative_discr = fx. bcx . ins ( ) . isub ( tag, niche_start) ;
178
- let cast_tag = clif_intcast ( fx, relative_discr, cast_to, false ) ;
179
- let is_niche = crate :: common:: codegen_icmp_imm (
180
- fx,
181
- IntCC :: UnsignedLessThanOrEqual ,
182
- relative_discr,
183
- i128:: from ( relative_max) ,
184
- ) ;
185
- ( is_niche, cast_tag, niche_variants. start ( ) . as_u32 ( ) as u128 )
186
- } ;
166
+ // General case.
167
+ let discr = fx. bcx . ins ( ) . isub ( tag, niche_start_value) ;
168
+ let tagged_discr = clif_intcast ( fx, discr, cast_to, false ) ;
169
+ if niche_variants. contains ( & untagged_variant) {
170
+ let is_niche = crate :: common:: codegen_icmp_imm (
171
+ fx,
172
+ IntCC :: UnsignedLessThan ,
173
+ discr,
174
+ ( discr_len - 1 ) as i128 ,
175
+ ) ;
176
+ let adj_untagged_idx =
177
+ untagged_variant. index ( ) - niche_variants. start ( ) . index ( ) ;
178
+ let untagged_delta = 1 + adj_untagged_idx;
179
+ let untagged_delta = match cast_to {
180
+ types:: I128 => {
181
+ let lsb = fx. bcx . ins ( ) . iconst ( types:: I64 , untagged_delta as i64 ) ;
182
+ let msb = fx. bcx . ins ( ) . iconst ( types:: I64 , 0 ) ;
183
+ fx. bcx . ins ( ) . iconcat ( lsb, msb)
184
+ }
185
+ ty => fx. bcx . ins ( ) . iconst ( ty, untagged_delta as i64 ) ,
186
+ } ;
187
+ let tagged_discr = fx. bcx . ins ( ) . iadd ( tagged_discr, untagged_delta) ;
187
188
188
- let tagged_discr = if delta == 0 {
189
- tagged_discr
190
- } else {
191
- let delta = match cast_to {
192
- types:: I128 => {
193
- let lsb = fx. bcx . ins ( ) . iconst ( types:: I64 , delta as u64 as i64 ) ;
194
- let msb = fx. bcx . ins ( ) . iconst ( types:: I64 , ( delta >> 64 ) as u64 as i64 ) ;
195
- fx. bcx . ins ( ) . iconcat ( lsb, msb)
196
- }
197
- ty => fx. bcx . ins ( ) . iconst ( ty, delta as i64 ) ,
198
- } ;
199
- fx. bcx . ins ( ) . iadd ( tagged_discr, delta)
189
+ let discr_len = match cast_to {
190
+ types:: I128 => {
191
+ let lsb = fx. bcx . ins ( ) . iconst ( types:: I64 , discr_len as i64 ) ;
192
+ let msb = fx. bcx . ins ( ) . iconst ( types:: I64 , 0 ) ;
193
+ fx. bcx . ins ( ) . iconcat ( lsb, msb)
194
+ }
195
+ ty => fx. bcx . ins ( ) . iconst ( ty, discr_len as i64 ) ,
196
+ } ;
197
+ let tagged_discr = fx. bcx . ins ( ) . urem ( tagged_discr, discr_len) ;
198
+
199
+ let niche_variants_start = niche_variants. start ( ) . index ( ) ;
200
+ let niche_variants_start = match cast_to {
201
+ types:: I128 => {
202
+ let lsb = fx. bcx . ins ( ) . iconst ( types:: I64 , niche_variants_start as i64 ) ;
203
+ let msb = fx. bcx . ins ( ) . iconst ( types:: I64 , 0 ) ;
204
+ fx. bcx . ins ( ) . iconcat ( lsb, msb)
205
+ }
206
+ ty => fx. bcx . ins ( ) . iconst ( ty, niche_variants_start as i64 ) ,
207
+ } ;
208
+ let tagged_discr = fx. bcx . ins ( ) . iadd ( tagged_discr, niche_variants_start) ;
209
+ ( is_niche, tagged_discr)
210
+ } else {
211
+ let is_niche = crate :: common:: codegen_icmp_imm (
212
+ fx,
213
+ IntCC :: UnsignedLessThan ,
214
+ discr,
215
+ ( discr_len - 1 ) as i128 ,
216
+ ) ;
217
+ let niche_variants_start = niche_variants. start ( ) . index ( ) ;
218
+ let niche_variants_start = match cast_to {
219
+ types:: I128 => {
220
+ let lsb = fx. bcx . ins ( ) . iconst ( types:: I64 , niche_variants_start as i64 ) ;
221
+ let msb = fx. bcx . ins ( ) . iconst ( types:: I64 , 0 ) ;
222
+ fx. bcx . ins ( ) . iconcat ( lsb, msb)
223
+ }
224
+ ty => fx. bcx . ins ( ) . iconst ( ty, niche_variants_start as i64 ) ,
225
+ } ;
226
+ let tagged_discr = fx. bcx . ins ( ) . iadd ( tagged_discr, niche_variants_start) ;
227
+ ( is_niche, tagged_discr)
228
+ }
200
229
} ;
201
230
202
231
let untagged_variant = if cast_to == types:: I128 {
0 commit comments