@@ -209,7 +209,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
209209 bx : & mut Bx ,
210210 cast_to : Ty < ' tcx > ,
211211 ) -> V {
212- let cast_to = bx. cx ( ) . immediate_backend_type ( bx. cx ( ) . layout_of ( cast_to) ) ;
212+ let cast_to_layout = bx. cx ( ) . layout_of ( cast_to) ;
213+ let cast_to_size = cast_to_layout. layout . size ( ) ;
214+ let cast_to = bx. cx ( ) . immediate_backend_type ( cast_to_layout) ;
213215 if self . layout . abi . is_uninhabited ( ) {
214216 return bx. cx ( ) . const_undef ( cast_to) ;
215217 }
@@ -229,7 +231,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
229231
230232 // Read the tag/niche-encoded discriminant from memory.
231233 let tag = self . project_field ( bx, tag_field) ;
232- let tag = bx. load_operand ( tag) ;
234+ let tag_op = bx. load_operand ( tag) ;
235+ let tag_imm = tag_op. immediate ( ) ;
233236
234237 // Decode the discriminant (specifically if it's niche-encoded).
235238 match * tag_encoding {
@@ -242,68 +245,161 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
242245 Int ( _, signed) => !tag_scalar. is_bool ( ) && signed,
243246 _ => false ,
244247 } ;
245- bx. intcast ( tag . immediate ( ) , cast_to, signed)
248+ bx. intcast ( tag_imm , cast_to, signed)
246249 }
247250 TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } => {
248- // Rebase from niche values to discriminants, and check
249- // whether the result is in range for the niche variants.
250- let niche_llty = bx. cx ( ) . immediate_backend_type ( tag. layout ) ;
251- let tag = tag. immediate ( ) ;
252-
253- // We first compute the "relative discriminant" (wrt `niche_variants`),
254- // that is, if `n = niche_variants.end() - niche_variants.start()`,
255- // we remap `niche_start..=niche_start + n` (which may wrap around)
256- // to (non-wrap-around) `0..=n`, to be able to check whether the
257- // discriminant corresponds to a niche variant with one comparison.
258- // We also can't go directly to the (variant index) discriminant
259- // and check that it is in the range `niche_variants`, because
260- // that might not fit in the same type, on top of needing an extra
261- // comparison (see also the comment on `let niche_discr`).
262- let relative_discr = if niche_start == 0 {
263- // Avoid subtracting `0`, which wouldn't work for pointers.
264- // FIXME(eddyb) check the actual primitive type here.
265- tag
251+ // Cast to an integer so we don't have to treat a pointer as a
252+ // special case.
253+ let ( tag, tag_llty) = if tag_scalar. primitive ( ) . is_ptr ( ) {
254+ let t = bx. type_isize ( ) ;
255+ let tag = bx. ptrtoint ( tag_imm, t) ;
256+ ( tag, t)
266257 } else {
267- bx . sub ( tag , bx. cx ( ) . const_uint_big ( niche_llty , niche_start ) )
258+ ( tag_imm , bx. cx ( ) . immediate_backend_type ( tag_op . layout ) )
268259 } ;
260+
261+ let tag_size = tag_scalar. size ( bx. cx ( ) ) ;
262+ let max_unsigned = tag_size. unsigned_int_max ( ) ;
263+ let max_signed = tag_size. signed_int_max ( ) as u128 ;
264+ let min_signed = max_signed + 1 ;
269265 let relative_max = niche_variants. end ( ) . as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ;
270- let is_niche = if relative_max == 0 {
271- // Avoid calling `const_uint`, which wouldn't work for pointers.
272- // Also use canonical == 0 instead of non-canonical u<= 0.
273- // FIXME(eddyb) check the actual primitive type here.
274- bx. icmp ( IntPredicate :: IntEQ , relative_discr, bx. cx ( ) . const_null ( niche_llty) )
266+ let niche_end = niche_start. wrapping_add ( relative_max as u128 ) & max_unsigned;
267+ let range = tag_scalar. valid_range ( bx. cx ( ) ) ;
268+
269+ let sle = |lhs : u128 , rhs : u128 | -> bool {
270+ // Signed and unsigned comparisons give the same results,
271+ // except that in signed comparisons an integer with the
272+ // sign bit set is less than one with the sign bit clear.
273+ // Toggle the sign bit to do a signed comparison.
274+ ( lhs ^ min_signed) <= ( rhs ^ min_signed)
275+ } ;
276+
277+ // We have a subrange `niche_start..=niche_end` inside `range`.
278+ // If the value of the tag is inside this subrange, it's a
279+ // "niche value", an increment of the discriminant. Otherwise it
280+ // indicates the untagged variant.
281+ // A general algorithm to extract the discriminant from the tag
282+ // is:
283+ // relative_tag = tag - niche_start
284+ // is_niche = relative_tag <= (ule) relative_max
285+ // discr = if is_niche {
286+ // cast(relative_tag) + niche_variants.start()
287+ // } else {
288+ // untagged_variant
289+ // }
290+ // However, we will likely be able to emit simpler code.
291+
292+ // Find the least and greatest values in `range`, considered
293+ // both as signed and unsigned.
294+ let ( low_unsigned, high_unsigned) = if range. start <= range. end {
295+ ( range. start , range. end )
296+ } else {
297+ ( 0 , max_unsigned)
298+ } ;
299+ let ( low_signed, high_signed) = if sle ( range. start , range. end ) {
300+ ( range. start , range. end )
275301 } else {
276- let relative_max = bx. cx ( ) . const_uint ( niche_llty, relative_max as u64 ) ;
277- bx. icmp ( IntPredicate :: IntULE , relative_discr, relative_max)
302+ ( min_signed, max_signed)
303+ } ;
304+
305+ let niches_ule = niche_start <= niche_end;
306+ let niches_sle = sle ( niche_start, niche_end) ;
307+ let cast_smaller = cast_to_size <= tag_size;
308+
309+ // In the algorithm above, we can change
310+ // cast(relative_tag) + niche_variants.start()
311+ // into
312+ // cast(tag) + (niche_variants.start() - niche_start)
313+ // if either the casted type is no larger than the original
314+ // type, or if the niche values are contiguous (in either the
315+ // signed or unsigned sense).
316+ let can_incr_after_cast = cast_smaller || niches_ule || niches_sle;
317+
318+ let data_for_boundary_niche = || -> Option < ( IntPredicate , u128 ) > {
319+ if !can_incr_after_cast {
320+ None
321+ } else if niche_start == low_unsigned {
322+ Some ( ( IntPredicate :: IntULE , niche_end) )
323+ } else if niche_end == high_unsigned {
324+ Some ( ( IntPredicate :: IntUGE , niche_start) )
325+ } else if niche_start == low_signed {
326+ Some ( ( IntPredicate :: IntSLE , niche_end) )
327+ } else if niche_end == high_signed {
328+ Some ( ( IntPredicate :: IntSGE , niche_start) )
329+ } else {
330+ None
331+ }
278332 } ;
279333
280- // NOTE(eddyb) this addition needs to be performed on the final
281- // type, in case the niche itself can't represent all variant
282- // indices (e.g. `u8` niche with more than `256` variants,
283- // but enough uninhabited variants so that the remaining variants
284- // fit in the niche).
285- // In other words, `niche_variants.end - niche_variants.start`
286- // is representable in the niche, but `niche_variants.end`
287- // might not be, in extreme cases.
288- let niche_discr = {
289- let relative_discr = if relative_max == 0 {
290- // HACK(eddyb) since we have only one niche, we know which
291- // one it is, and we can avoid having a dynamic value here.
292- bx. cx ( ) . const_uint ( cast_to, 0 )
334+ let ( is_niche, tagged_discr, delta) = if relative_max == 0 {
335+ // Best case scenario: only one tagged variant. This will
336+ // likely become just a comparison and a jump.
337+ // The algorithm is:
338+ // is_niche = tag == niche_start
339+ // discr = if is_niche {
340+ // niche_start
341+ // } else {
342+ // untagged_variant
343+ // }
344+ let niche_start = bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ;
345+ let is_niche = bx. icmp ( IntPredicate :: IntEQ , tag, niche_start) ;
346+ let tagged_discr =
347+ bx. cx ( ) . const_uint ( cast_to, niche_variants. start ( ) . as_u32 ( ) as u64 ) ;
348+ ( is_niche, tagged_discr, 0 )
349+ } else if let Some ( ( predicate, constant) ) = data_for_boundary_niche ( ) {
350+ // The niche values are either the lowest or the highest in
351+ // `range`. We can avoid the first subtraction in the
352+ // algorithm.
353+ // The algorithm is now this:
354+ // is_niche = tag <= niche_end
355+ // discr = if is_niche {
356+ // cast(tag) + (niche_variants.start() - niche_start)
357+ // } else {
358+ // untagged_variant
359+ // }
360+ // (the first line may instead be tag >= niche_start,
361+ // and may be a signed or unsigned comparison)
362+ let is_niche =
363+ bx. icmp ( predicate, tag, bx. cx ( ) . const_uint_big ( tag_llty, constant) ) ;
364+ let cast_tag = if cast_smaller {
365+ bx. intcast ( tag, cast_to, false )
366+ } else if niches_ule {
367+ bx. zext ( tag, cast_to)
293368 } else {
294- bx. intcast ( relative_discr , cast_to, false )
369+ bx. sext ( tag , cast_to)
295370 } ;
296- bx. add (
371+
372+ let delta = ( niche_variants. start ( ) . as_u32 ( ) as u128 ) . wrapping_sub ( niche_start) ;
373+ ( is_niche, cast_tag, delta)
374+ } else {
375+ // The special cases don't apply, so we'll have to go with
376+ // the general algorithm.
377+ let relative_discr = bx. sub ( tag, bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ) ;
378+ let cast_tag = bx. intcast ( relative_discr, cast_to, false ) ;
379+ let is_niche = bx. icmp (
380+ IntPredicate :: IntULE ,
297381 relative_discr,
298- bx. cx ( ) . const_uint ( cast_to, niche_variants. start ( ) . as_u32 ( ) as u64 ) ,
299- )
382+ bx. cx ( ) . const_uint ( tag_llty, relative_max as u64 ) ,
383+ ) ;
384+ ( is_niche, cast_tag, niche_variants. start ( ) . as_u32 ( ) as u128 )
300385 } ;
301386
302- bx. select (
387+ let tagged_discr = if delta == 0 {
388+ tagged_discr
389+ } else {
390+ bx. add ( tagged_discr, bx. cx ( ) . const_uint_big ( cast_to, delta) )
391+ } ;
392+
393+ let discr = bx. select (
303394 is_niche,
304- niche_discr ,
395+ tagged_discr ,
305396 bx. cx ( ) . const_uint ( cast_to, untagged_variant. as_u32 ( ) as u64 ) ,
306- )
397+ ) ;
398+
399+ // In principle we could insert assumes on the possible range of `discr`, but
400+ // currently in LLVM this seems to be a pessimization.
401+
402+ discr
307403 }
308404 }
309405 }
0 commit comments