@@ -209,7 +209,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
209
209
bx : & mut Bx ,
210
210
cast_to : Ty < ' tcx > ,
211
211
) -> 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) ;
213
215
if self . layout . abi . is_uninhabited ( ) {
214
216
return bx. cx ( ) . const_undef ( cast_to) ;
215
217
}
@@ -229,7 +231,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
229
231
230
232
// Read the tag/niche-encoded discriminant from memory.
231
233
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 ( ) ;
233
236
234
237
// Decode the discriminant (specifically if it's niche-encoded).
235
238
match * tag_encoding {
@@ -242,68 +245,161 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
242
245
Int ( _, signed) => !tag_scalar. is_bool ( ) && signed,
243
246
_ => false ,
244
247
} ;
245
- bx. intcast ( tag . immediate ( ) , cast_to, signed)
248
+ bx. intcast ( tag_imm , cast_to, signed)
246
249
}
247
250
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)
266
257
} 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 ) )
268
259
} ;
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 ;
269
265
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 )
275
301
} 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
+ }
278
332
} ;
279
333
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)
293
368
} else {
294
- bx. intcast ( relative_discr , cast_to, false )
369
+ bx. sext ( tag , cast_to)
295
370
} ;
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 ,
297
381
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 )
300
385
} ;
301
386
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 (
303
394
is_niche,
304
- niche_discr ,
395
+ tagged_discr ,
305
396
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
307
403
}
308
404
}
309
405
}
0 commit comments