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