@@ -52,10 +52,20 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
5252 variants : _,
5353 } => {
5454 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+
5558 let niche = place. place_field ( fx, FieldIdx :: new ( tag_field) ) ;
5659 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) ;
5969 let niche_value = match niche_type {
6070 types:: I128 => {
6171 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>(
131141 dest. write_cvalue ( fx, res) ;
132142 }
133143 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.
161161 let is_niche = codegen_icmp_imm ( fx, IntCC :: Equal , tag, niche_start as i128 ) ;
162162 let tagged_discr =
163163 fx. bcx . ins ( ) . iconst ( cast_to, niche_variants. start ( ) . as_u32 ( ) as i64 ) ;
164- ( is_niche, tagged_discr, 0 )
164+ ( is_niche, tagged_discr)
165165 } 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) ;
187188
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+ }
200229 } ;
201230
202231 let untagged_variant = if cast_to == types:: I128 {
0 commit comments