@@ -51,10 +51,19 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
5151            variants :  _, 
5252        }  => { 
5353            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+ 
5457                let  niche = place. place_field ( fx,  FieldIdx :: new ( tag_field) ) ; 
5558                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 = untagged_variant. index ( )  - niche_variants. start ( ) . index ( ) ; 
62+                     ( adj_idx + discr_len - adj_untagged_idx)  % discr_len - 1 ; 
63+                 }  else  { 
64+                     adj_idx
65+                 } ; 
66+                 let  niche_value = ( discr as  u128 ) . wrapping_add ( niche_start) ; 
5867                let  niche_value = match  niche_type { 
5968                    types:: I128  => { 
6069                        let  lsb = fx. bcx . ins ( ) . iconst ( types:: I64 ,  niche_value as  u64  as  i64 ) ; 
@@ -130,73 +139,92 @@ pub(crate) fn codegen_get_discriminant<'tcx>(
130139            dest. write_cvalue ( fx,  res) ; 
131140        } 
132141        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 ) ; 
142+             // See the algorithm explanation in the definition of `TagEncoding::Niche`. 
143+             let  discr_len = niche_variants. end ( ) . index ( )  - niche_variants. start ( ) . index ( )  + 1 ; 
144+ 
145+             let  niche_start = match  fx. bcx . func . dfg . value_type ( tag)  { 
146+                 types:: I128  => { 
147+                     let  lsb = fx. bcx . ins ( ) . iconst ( types:: I64 ,  niche_start as  u64  as  i64 ) ; 
148+                     let  msb =
149+                         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) ; 
161161                let  tagged_discr =
162162                    fx. bcx . ins ( ) . iconst ( cast_to,  niche_variants. start ( ) . as_u32 ( )  as  i64 ) ; 
163-                 ( is_niche,  tagged_discr,   0 ) 
163+                 ( is_niche,  tagged_discr) 
164164            }  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 = untagged_variant . index ( )  - niche_variants . start ( ) . index ( ) ; 
176+                      let  untagged_delta  = 1  + adj_untagged_idx ; 
177+                      let  untagged_delta  = match   cast_to  { 
178+                         types :: I128  =>  { 
179+                              let  lsb = fx . bcx . ins ( ) . iconst ( types :: I64 ,  untagged_delta  as   u64   as   i64 ) ; 
180+                              let  msb = fx . bcx . ins ( ) . iconst ( types :: I64 ,   ( untagged_delta >>  64 )   as   u64   as   i64 ) ; 
181+                             fx . bcx . ins ( ) . iconcat ( lsb ,  msb ) 
182+                          } 
183+                         ty => fx . bcx . ins ( ) . iconst ( ty ,  untagged_delta  as   i64 ) , 
184+                      } ; 
185+                      let  tagged_discr = fx . bcx . ins ( ) . iadd ( tagged_discr ,  untagged_delta ) ; 
186186
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 ) , 
187+                     let  discr_len = match  cast_to { 
188+                         types:: I128  => { 
189+                             let  lsb = fx. bcx . ins ( ) . iconst ( types:: I64 ,  discr_len as  u64  as  i64 ) ; 
190+                             let  msb = fx. bcx . ins ( ) . iconst ( types:: I64 ,  ( discr_len >> 64 )  as  u64  as  i64 ) ; 
191+                             fx. bcx . ins ( ) . iconcat ( lsb,  msb) 
192+                         } 
193+                         ty => fx. bcx . ins ( ) . iconst ( ty,  discr_len as  i64 ) , 
194+                     } ; 
195+                     let  tagged_discr = fx. bcx . ins ( ) . urem ( tagged_discr,  discr_len) ; 
196+ 
197+                     let  niche_variants_start = niche_variants. start ( ) . index ( ) ; 
198+                     let  niche_variants_start = match  cast_to { 
199+                         types:: I128  => { 
200+                             let  lsb = fx. bcx . ins ( ) . iconst ( types:: I64 ,  niche_variants_start as  u64  as  i64 ) ; 
201+                             let  msb = fx. bcx . ins ( ) . iconst ( types:: I64 ,  ( niche_variants_start >> 64 )  as  u64  as  i64 ) ; 
202+                             fx. bcx . ins ( ) . iconcat ( lsb,  msb) 
203+                         } 
204+                         ty => fx. bcx . ins ( ) . iconst ( ty,  niche_variants_start as  i64 ) , 
205+                     } ; 
206+                     let  tagged_discr = fx. bcx . ins ( ) . iadd ( tagged_discr,  niche_variants_start) ; 
207+                     ( is_niche,  tagged_discr) 
208+                 }  else  { 
209+                     let  is_niche = crate :: common:: codegen_icmp_imm ( 
210+                         fx, 
211+                         IntCC :: UnsignedLessThan , 
212+                         discr, 
213+                         i128:: from ( discr_len - 1 ) , 
214+                     ) ; 
215+                     let  niche_variants_start = niche_variants. start ( ) . index ( ) ; 
216+                     let  niche_variants_start = match  cast_to { 
217+                         types:: I128  => { 
218+                             let  lsb = fx. bcx . ins ( ) . iconst ( types:: I64 ,  niche_variants_start as  u64  as  i64 ) ; 
219+                             let  msb = fx. bcx . ins ( ) . iconst ( types:: I64 ,  ( niche_variants_start >> 64 )  as  u64  as  i64 ) ; 
220+                             fx. bcx . ins ( ) . iconcat ( lsb,  msb) 
221+                         } 
222+                         ty => fx. bcx . ins ( ) . iconst ( ty,  niche_variants_start as  i64 ) , 
223+                     } ; 
224+                     let  tagged_discr = fx. bcx . ins ( ) . iadd ( tagged_discr,  niche_variants_start) ; 
225+                     ( is_niche,  tagged_discr) 
197226                } ; 
198-                 fx. bcx . ins ( ) . iadd ( tagged_discr,  delta) 
199-             } ; 
227+             } 
200228
201229            let  untagged_variant = if  cast_to == types:: I128  { 
202230                let  zero = fx. bcx . ins ( ) . iconst ( types:: I64 ,  0 ) ; 
0 commit comments