1
1
use super :: operand:: OperandValue ;
2
2
use super :: { FunctionCx , LocalRef } ;
3
3
4
- use crate :: common:: IntPredicate ;
4
+ use crate :: common:: { IntPredicate , TypeKind } ;
5
5
use crate :: glue;
6
6
use crate :: traits:: * ;
7
7
@@ -227,13 +227,13 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
227
227
}
228
228
} ;
229
229
230
- // Read the tag/niche-encoded discriminant from memory.
231
- let tag = self . project_field ( bx, tag_field) ;
232
- let tag = bx. load_operand ( tag) ;
230
+ let tag_place = self . project_field ( bx, tag_field) ;
233
231
234
232
// Decode the discriminant (specifically if it's niche-encoded).
235
233
match * tag_encoding {
236
234
TagEncoding :: Direct => {
235
+ // Read the tag from memory.
236
+ let tag = bx. load_operand ( tag_place) ;
237
237
let signed = match tag_scalar. primitive ( ) {
238
238
// We use `i1` for bytes that are always `0` or `1`,
239
239
// e.g., `#[repr(i8)] enum E { A, B }`, but we can't
@@ -244,11 +244,30 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
244
244
} ;
245
245
bx. intcast ( tag. immediate ( ) , cast_to, signed)
246
246
}
247
- 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 ( ) ;
247
+ TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start, ref flag, } => {
248
+ let read = |bx : & mut Bx , place : Self | -> ( V , <Bx as BackendTypes >:: Type ) {
249
+ let ty = bx. cx ( ) . immediate_backend_type ( place. layout ) ;
250
+ let op = bx. load_operand ( place) ;
251
+ let val = op. immediate ( ) ;
252
+ if bx. cx ( ) . type_kind ( ty) == TypeKind :: Pointer {
253
+ let new_ty = bx. cx ( ) . type_isize ( ) ;
254
+ let new_val = bx. ptrtoint ( val, new_ty) ;
255
+ ( new_val, new_ty)
256
+ } else {
257
+ ( val, ty)
258
+ }
259
+ } ;
260
+
261
+ let ( tag, niche_llty) = read ( bx, tag_place) ;
262
+
263
+ let ( untagged_in_niche, flag_eq_magic_value_opt) = if let Some ( flag) = flag {
264
+ let flag_place = self . project_field ( bx, flag. field ) ;
265
+ let ( flag_imm, flag_llty) = read ( bx, flag_place) ;
266
+ let magic_value = bx. cx ( ) . const_uint_big ( flag_llty, flag. magic_value ) ;
267
+ ( flag. untagged_in_niche , Some ( bx. icmp ( IntPredicate :: IntEQ , flag_imm, magic_value) ) )
268
+ } else {
269
+ ( true , None )
270
+ } ;
252
271
253
272
// We first compute the "relative discriminant" (wrt `niche_variants`),
254
273
// that is, if `n = niche_variants.end() - niche_variants.start()`,
@@ -259,23 +278,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
259
278
// and check that it is in the range `niche_variants`, because
260
279
// that might not fit in the same type, on top of needing an extra
261
280
// 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
266
- } else {
267
- bx. sub ( tag, bx. cx ( ) . const_uint_big ( niche_llty, niche_start) )
268
- } ;
281
+ let relative_discr = bx. sub ( tag, bx. cx ( ) . const_uint_big ( niche_llty, niche_start) ) ;
269
282
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) )
275
- } else {
276
- let relative_max = bx. cx ( ) . const_uint ( niche_llty, relative_max as u64 ) ;
277
- bx. icmp ( IntPredicate :: IntULE , relative_discr, relative_max)
278
- } ;
279
283
280
284
// NOTE(eddyb) this addition needs to be performed on the final
281
285
// type, in case the niche itself can't represent all variant
@@ -285,7 +289,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
285
289
// In other words, `niche_variants.end - niche_variants.start`
286
290
// is representable in the niche, but `niche_variants.end`
287
291
// might not be, in extreme cases.
288
- let niche_discr = {
292
+ let potential_niche_discr = {
289
293
let relative_discr = if relative_max == 0 {
290
294
// HACK(eddyb) since we have only one niche, we know which
291
295
// one it is, and we can avoid having a dynamic value here.
@@ -299,11 +303,29 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
299
303
)
300
304
} ;
301
305
302
- bx. select (
303
- is_niche,
304
- niche_discr,
305
- bx. cx ( ) . const_uint ( cast_to, untagged_variant. as_u32 ( ) as u64 ) ,
306
- )
306
+ let untagged_discr = bx. cx ( ) . const_uint ( cast_to, untagged_variant. as_u32 ( ) as u64 ) ;
307
+
308
+ let niche_discr = if untagged_in_niche {
309
+ let relative_max_const = bx. cx ( ) . const_uint ( niche_llty, relative_max as u64 ) ;
310
+ let is_niche = bx. icmp ( IntPredicate :: IntULE , relative_discr, relative_max_const) ;
311
+ bx. select (
312
+ is_niche,
313
+ potential_niche_discr,
314
+ untagged_discr,
315
+ )
316
+ } else {
317
+ potential_niche_discr
318
+ } ;
319
+
320
+ if let Some ( flag_eq_magic_value) = flag_eq_magic_value_opt {
321
+ bx. select (
322
+ flag_eq_magic_value,
323
+ niche_discr,
324
+ untagged_discr,
325
+ )
326
+ } else {
327
+ niche_discr
328
+ }
307
329
}
308
330
}
309
331
}
@@ -337,23 +359,30 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
337
359
}
338
360
Variants :: Multiple {
339
361
tag_encoding :
340
- TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } ,
362
+ TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start, ref flag , } ,
341
363
tag_field,
342
364
..
343
365
} => {
366
+ let store = |bx : & mut Bx , value : u128 , place : Self | {
367
+ let ty = bx. cx ( ) . immediate_backend_type ( place. layout ) ;
368
+ let val = if bx. cx ( ) . type_kind ( ty) == TypeKind :: Pointer {
369
+ let ty_isize = bx. cx ( ) . type_isize ( ) ;
370
+ let llvalue = bx. cx ( ) . const_uint_big ( ty_isize, value) ;
371
+ bx. inttoptr ( llvalue, ty)
372
+ } else {
373
+ bx. cx ( ) . const_uint_big ( ty, value)
374
+ } ;
375
+ OperandValue :: Immediate ( val) . store ( bx, place) ;
376
+ } ;
344
377
if variant_index != untagged_variant {
378
+ if let Some ( flag) = flag {
379
+ let place = self . project_field ( bx, flag. field ) ;
380
+ store ( bx, flag. magic_value , place) ;
381
+ }
345
382
let niche = self . project_field ( bx, tag_field) ;
346
- let niche_llty = bx. cx ( ) . immediate_backend_type ( niche. layout ) ;
347
383
let niche_value = variant_index. as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ;
348
384
let niche_value = ( niche_value as u128 ) . wrapping_add ( niche_start) ;
349
- // FIXME(eddyb): check the actual primitive type here.
350
- let niche_llval = if niche_value == 0 {
351
- // HACK(eddyb): using `c_null` as it works on all types.
352
- bx. cx ( ) . const_null ( niche_llty)
353
- } else {
354
- bx. cx ( ) . const_uint_big ( niche_llty, niche_value)
355
- } ;
356
- OperandValue :: Immediate ( niche_llval) . store ( bx, niche) ;
385
+ store ( bx, niche_value, niche) ;
357
386
}
358
387
}
359
388
}
0 commit comments