@@ -5,10 +5,10 @@ use super::CodegenCx;
5
5
use crate :: abi:: ConvSpirvType ;
6
6
use crate :: builder_spirv:: { SpirvConst , SpirvValue , SpirvValueExt , SpirvValueKind } ;
7
7
use crate :: spirv_type:: SpirvType ;
8
+ use itertools:: Itertools as _;
8
9
use rspirv:: spirv:: Word ;
9
10
use rustc_abi:: { self as abi, AddressSpace , Float , HasDataLayout , Integer , Primitive , Size } ;
10
11
use rustc_codegen_ssa:: traits:: { ConstCodegenMethods , MiscCodegenMethods , StaticCodegenMethods } ;
11
- use rustc_middle:: bug;
12
12
use rustc_middle:: mir:: interpret:: { ConstAllocation , GlobalAlloc , Scalar , alloc_range} ;
13
13
use rustc_middle:: ty:: layout:: LayoutOf ;
14
14
use rustc_span:: { DUMMY_SP , Span } ;
@@ -255,7 +255,11 @@ impl ConstCodegenMethods for CodegenCx<'_> {
255
255
other. debug( ty, self )
256
256
) ) ,
257
257
} ;
258
- let init = self . create_const_alloc ( alloc, pointee) ;
258
+ // FIXME(eddyb) always use `const_data_from_alloc`, and
259
+ // defer the actual `try_read_from_const_alloc` step.
260
+ let init = self
261
+ . try_read_from_const_alloc ( alloc, pointee)
262
+ . unwrap_or_else ( || self . const_data_from_alloc ( alloc) ) ;
259
263
let value = self . static_addr_of ( init, alloc. inner ( ) . align , None ) ;
260
264
( value, AddressSpace :: DATA )
261
265
}
@@ -280,7 +284,11 @@ impl ConstCodegenMethods for CodegenCx<'_> {
280
284
other. debug( ty, self )
281
285
) ) ,
282
286
} ;
283
- let init = self . create_const_alloc ( alloc, pointee) ;
287
+ // FIXME(eddyb) always use `const_data_from_alloc`, and
288
+ // defer the actual `try_read_from_const_alloc` step.
289
+ let init = self
290
+ . try_read_from_const_alloc ( alloc, pointee)
291
+ . unwrap_or_else ( || self . const_data_from_alloc ( alloc) ) ;
284
292
let value = self . static_addr_of ( init, alloc. inner ( ) . align , None ) ;
285
293
( value, AddressSpace :: DATA )
286
294
}
@@ -348,9 +356,8 @@ impl<'tcx> CodegenCx<'tcx> {
348
356
&& let Some ( SpirvConst :: ConstDataFromAlloc ( alloc) ) =
349
357
self . builder . lookup_const_by_id ( pointee)
350
358
&& let SpirvType :: Pointer { pointee } = self . lookup_type ( ty)
359
+ && let Some ( init) = self . try_read_from_const_alloc ( alloc, pointee)
351
360
{
352
- let mut offset = Size :: ZERO ;
353
- let init = self . read_from_const_alloc ( alloc, & mut offset, pointee) ;
354
361
return self . static_addr_of ( init, alloc. inner ( ) . align , None ) ;
355
362
}
356
363
@@ -379,44 +386,38 @@ impl<'tcx> CodegenCx<'tcx> {
379
386
}
380
387
}
381
388
382
- pub fn create_const_alloc ( & self , alloc : ConstAllocation < ' tcx > , ty : Word ) -> SpirvValue {
383
- tracing:: trace!(
384
- "Creating const alloc of type {} with {} bytes" ,
385
- self . debug_type( ty) ,
386
- alloc. inner( ) . len( )
387
- ) ;
388
- let mut offset = Size :: ZERO ;
389
- let result = self . read_from_const_alloc ( alloc, & mut offset, ty) ;
390
- assert_eq ! (
391
- offset. bytes_usize( ) ,
392
- alloc. inner( ) . len( ) ,
393
- "create_const_alloc must consume all bytes of an Allocation"
394
- ) ;
395
- tracing:: trace!( "Done creating alloc of type {}" , self . debug_type( ty) ) ;
396
- result
397
- }
398
-
399
- fn read_from_const_alloc (
389
+ /// Attempt to read a whole constant of type `ty` from `alloc`, but only
390
+ /// returning that constant if its size covers the entirety of `alloc`.
391
+ //
392
+ // FIXME(eddyb) should this use something like `Result<_, PartialRead>`?
393
+ pub fn try_read_from_const_alloc (
400
394
& self ,
401
395
alloc : ConstAllocation < ' tcx > ,
402
- offset : & mut Size ,
403
396
ty : Word ,
404
- ) -> SpirvValue {
405
- let ty_concrete = self . lookup_type ( ty) ;
406
- * offset = offset. align_to ( ty_concrete. alignof ( self ) ) ;
407
- // these print statements are really useful for debugging, so leave them easily available
408
- // println!("const at {}: {}", offset.bytes(), self.debug_type(ty));
409
- match ty_concrete {
410
- SpirvType :: Void => self
411
- . tcx
412
- . dcx ( )
413
- . fatal ( "cannot create const alloc of type void" ) ,
397
+ ) -> Option < SpirvValue > {
398
+ let ( result, read_size) = self . read_from_const_alloc_at ( alloc, ty, Size :: ZERO ) ;
399
+ ( read_size == alloc. inner ( ) . size ( ) ) . then_some ( result)
400
+ }
401
+
402
+ // HACK(eddyb) the `Size` returned is the equivalent of `size_of_val` on
403
+ // the returned constant, i.e. `ty.sizeof()` can be either `Some(read_size)`,
404
+ // or `None` - i.e. unsized, in which case only the returned `Size` records
405
+ // how much was read from `alloc` to build the returned constant value.
406
+ #[ tracing:: instrument( level = "trace" , skip( self ) , fields( ty = ?self . debug_type( ty) , offset) ) ]
407
+ fn read_from_const_alloc_at (
408
+ & self ,
409
+ alloc : ConstAllocation < ' tcx > ,
410
+ ty : Word ,
411
+ offset : Size ,
412
+ ) -> ( SpirvValue , Size ) {
413
+ let ty_def = self . lookup_type ( ty) ;
414
+ match ty_def {
414
415
SpirvType :: Bool
415
416
| SpirvType :: Integer ( ..)
416
417
| SpirvType :: Float ( _)
417
418
| SpirvType :: Pointer { .. } => {
418
- let size = ty_concrete . sizeof ( self ) . unwrap ( ) ;
419
- let primitive = match ty_concrete {
419
+ let size = ty_def . sizeof ( self ) . unwrap ( ) ;
420
+ let primitive = match ty_def {
420
421
SpirvType :: Bool => Primitive :: Int ( Integer :: fit_unsigned ( 0 ) , false ) ,
421
422
SpirvType :: Integer ( int_size, int_signedness) => Primitive :: Int (
422
423
match int_size {
@@ -445,147 +446,132 @@ impl<'tcx> CodegenCx<'tcx> {
445
446
}
446
447
} ) ,
447
448
SpirvType :: Pointer { .. } => Primitive :: Pointer ( AddressSpace :: DATA ) ,
448
- unsupported_spirv_type => bug ! (
449
- "invalid spirv type internal to create_alloc_const2: {:?}" ,
450
- unsupported_spirv_type
451
- ) ,
449
+ _ => unreachable ! ( ) ,
452
450
} ;
453
- // alloc_id is not needed by read_scalar, so we just use 0. If the context
454
- // refers to a pointer, read_scalar will find the actual alloc_id. It
455
- // only uses the input alloc_id in the case that the scalar is uninitialized
456
- // as part of the error output
457
- // tldr, the pointer here is only needed for the offset
458
451
let value = match alloc. inner ( ) . read_scalar (
459
452
self ,
460
- alloc_range ( * offset, size) ,
453
+ alloc_range ( offset, size) ,
461
454
matches ! ( primitive, Primitive :: Pointer ( _) ) ,
462
455
) {
463
456
Ok ( scalar) => {
464
457
self . scalar_to_backend ( scalar, self . primitive_to_scalar ( primitive) , ty)
465
458
}
459
+ // FIXME(eddyb) this is really unsound, could be an error!
466
460
_ => self . undef ( ty) ,
467
461
} ;
468
- * offset += size;
469
- value
462
+ ( value, size)
470
463
}
471
464
SpirvType :: Adt {
472
- size,
473
465
field_types,
474
466
field_offsets,
475
467
..
476
468
} => {
477
- let base = * offset;
478
- let mut values = Vec :: with_capacity ( field_types. len ( ) ) ;
479
- let mut occupied_spaces = Vec :: with_capacity ( field_types. len ( ) ) ;
480
- for ( & ty, & field_offset) in field_types. iter ( ) . zip ( field_offsets. iter ( ) ) {
481
- let total_offset_start = base + field_offset;
482
- let mut total_offset_end = total_offset_start;
483
- values. push (
484
- self . read_from_const_alloc ( alloc, & mut total_offset_end, ty)
485
- . def_cx ( self ) ,
486
- ) ;
487
- occupied_spaces. push ( total_offset_start..total_offset_end) ;
488
- }
489
- if let Some ( size) = size {
490
- * offset += size;
491
- } else {
492
- assert_eq ! (
493
- offset. bytes_usize( ) ,
494
- alloc. inner( ) . len( ) ,
495
- "create_const_alloc must consume all bytes of an Allocation after an unsized struct"
469
+ // HACK(eddyb) this accounts for unsized `struct`s, and allows
470
+ // detecting gaps *only* at the end of the type, but is cheap.
471
+ let mut tail_read_range = ..Size :: ZERO ;
472
+ let result = self . constant_composite (
473
+ ty,
474
+ field_types
475
+ . iter ( )
476
+ . zip_eq ( field_offsets. iter ( ) )
477
+ . map ( |( & f_ty, & f_offset) | {
478
+ let ( f, f_size) =
479
+ self . read_from_const_alloc_at ( alloc, f_ty, offset + f_offset) ;
480
+ tail_read_range. end =
481
+ tail_read_range. end . max ( offset + f_offset + f_size) ;
482
+ f. def_cx ( self )
483
+ } ) ,
484
+ ) ;
485
+
486
+ let ty_size = ty_def. sizeof ( self ) ;
487
+
488
+ // HACK(eddyb) catch non-padding holes in e.g. `enum` values.
489
+ if let Some ( ty_size) = ty_size
490
+ && let Some ( tail_gap) = ( ty_size. bytes ( ) )
491
+ . checked_sub ( tail_read_range. end . align_to ( ty_def. alignof ( self ) ) . bytes ( ) )
492
+ && tail_gap > 0
493
+ {
494
+ self . zombie_no_span (
495
+ result. def_cx ( self ) ,
496
+ & format ! (
497
+ "undersized `{}` constant (at least {tail_gap} bytes may be missing)" ,
498
+ self . debug_type( ty)
499
+ ) ,
496
500
) ;
497
501
}
498
- self . constant_composite ( ty, values. into_iter ( ) )
499
- }
500
- SpirvType :: Array { element, count } => {
501
- let count = self . builder . lookup_const_scalar ( count) . unwrap ( ) as usize ;
502
- let values = ( 0 ..count) . map ( |_| {
503
- self . read_from_const_alloc ( alloc, offset, element)
504
- . def_cx ( self )
505
- } ) ;
506
- self . constant_composite ( ty, values)
507
- }
508
- SpirvType :: Vector { element, count } => {
509
- let total_size = ty_concrete
510
- . sizeof ( self )
511
- . expect ( "create_const_alloc: Vectors must be sized" ) ;
512
- let final_offset = * offset + total_size;
513
- let values = ( 0 ..count) . map ( |_| {
514
- self . read_from_const_alloc ( alloc, offset, element)
515
- . def_cx ( self )
516
- } ) ;
517
- let result = self . constant_composite ( ty, values) ;
518
- assert ! ( * offset <= final_offset) ;
519
- // Vectors sometimes have padding at the end (e.g. vec3), skip over it.
520
- * offset = final_offset;
521
- result
522
- }
523
- SpirvType :: Matrix { element, count } => {
524
- let total_size = ty_concrete
525
- . sizeof ( self )
526
- . expect ( "create_const_alloc: Matrices must be sized" ) ;
527
- let final_offset = * offset + total_size;
528
- let values = ( 0 ..count) . map ( |_| {
529
- self . read_from_const_alloc ( alloc, offset, element)
530
- . def_cx ( self )
531
- } ) ;
532
- let result = self . constant_composite ( ty, values) ;
533
- assert ! ( * offset <= final_offset) ;
534
- // Matrices sometimes have padding at the end (e.g. Mat4x3), skip over it.
535
- * offset = final_offset;
536
- result
502
+
503
+ ( result, ty_size. unwrap_or ( tail_read_range. end ) )
537
504
}
538
- SpirvType :: RuntimeArray { element } => {
539
- let mut values = Vec :: new ( ) ;
540
- while offset. bytes_usize ( ) != alloc. inner ( ) . len ( ) {
541
- values. push (
542
- self . read_from_const_alloc ( alloc, offset, element)
543
- . def_cx ( self ) ,
544
- ) ;
505
+ SpirvType :: Vector { element, .. }
506
+ | SpirvType :: Matrix { element, .. }
507
+ | SpirvType :: Array { element, .. }
508
+ | SpirvType :: RuntimeArray { element } => {
509
+ let stride = self . lookup_type ( element) . sizeof ( self ) . unwrap ( ) ;
510
+
511
+ let count = match ty_def {
512
+ SpirvType :: Vector { count, .. } | SpirvType :: Matrix { count, .. } => {
513
+ u64:: from ( count)
514
+ }
515
+ SpirvType :: Array { count, .. } => {
516
+ u64:: try_from ( self . builder . lookup_const_scalar ( count) . unwrap ( ) ) . unwrap ( )
517
+ }
518
+ SpirvType :: RuntimeArray { .. } => {
519
+ ( alloc. inner ( ) . size ( ) - offset) . bytes ( ) / stride. bytes ( )
520
+ }
521
+ _ => unreachable ! ( ) ,
522
+ } ;
523
+
524
+ let result = self . constant_composite (
525
+ ty,
526
+ ( 0 ..count) . map ( |i| {
527
+ let ( e, e_size) =
528
+ self . read_from_const_alloc_at ( alloc, element, offset + i * stride) ;
529
+ assert_eq ! ( e_size, stride) ;
530
+ e. def_cx ( self )
531
+ } ) ,
532
+ ) ;
533
+
534
+ // HACK(eddyb) `align_to` can only cause an increase for `Vector`,
535
+ // because its `size`/`align` are rounded up to a power of two
536
+ // (for now, at least, even if eventually that should go away).
537
+ let read_size = ( count * stride) . align_to ( ty_def. alignof ( self ) ) ;
538
+
539
+ if let Some ( ty_size) = ty_def. sizeof ( self ) {
540
+ assert_eq ! ( read_size, ty_size) ;
545
541
}
546
- let result = self . constant_composite ( ty, values. into_iter ( ) ) ;
547
- // TODO: Figure out how to do this. Compiling the below crashes both clspv *and* llvm-spirv:
548
- /*
549
- __constant struct A {
550
- float x;
551
- int y[];
552
- } a = {1, {2, 3, 4}};
553
-
554
- __kernel void foo(__global int* data, __constant int* c) {
555
- __constant struct A* asdf = &a;
556
- *data = *c + asdf->y[*c];
542
+
543
+ if let SpirvType :: RuntimeArray { .. } = ty_def {
544
+ // FIXME(eddyb) values of this type should never be created,
545
+ // the only reasonable encoding of e.g. `&str` consts should
546
+ // be `&[u8; N]` consts, with the `static_addr_of` pointer
547
+ // (*not* the value it points to) cast to `&str`, afterwards.
548
+ self . zombie_no_span (
549
+ result. def_cx ( self ) ,
550
+ & format ! ( "unsupported unsized `{}` constant" , self . debug_type( ty) ) ,
551
+ ) ;
557
552
}
558
- */
559
- // NOTE(eddyb) the above description is a bit outdated, it's now
560
- // clear `OpTypeRuntimeArray` does not belong in user code, and
561
- // is only for dynamically-sized SSBOs and descriptor indexing,
562
- // and a general solution looks similar to `union` handling, but
563
- // for the length of a fixed-length array.
564
- self . zombie_no_span ( result. def_cx ( self ) , "constant `OpTypeRuntimeArray` value" ) ;
565
- result
553
+
554
+ ( result, read_size)
555
+ }
556
+
557
+ SpirvType :: Void
558
+ | SpirvType :: Function { .. }
559
+ | SpirvType :: Image { .. }
560
+ | SpirvType :: Sampler
561
+ | SpirvType :: SampledImage { .. }
562
+ | SpirvType :: InterfaceBlock { .. }
563
+ | SpirvType :: AccelerationStructureKhr
564
+ | SpirvType :: RayQueryKhr => {
565
+ let result = self . undef ( ty) ;
566
+ self . zombie_no_span (
567
+ result. def_cx ( self ) ,
568
+ & format ! (
569
+ "cannot reinterpret Rust constant data as a `{}` value" ,
570
+ self . debug_type( ty)
571
+ ) ,
572
+ ) ;
573
+ ( result, ty_def. sizeof ( self ) . unwrap_or ( Size :: ZERO ) )
566
574
}
567
- SpirvType :: Function { .. } => self
568
- . tcx
569
- . dcx ( )
570
- . fatal ( "TODO: SpirvType::Function not supported yet in create_const_alloc" ) ,
571
- SpirvType :: Image { .. } => self . tcx . dcx ( ) . fatal ( "cannot create a constant image value" ) ,
572
- SpirvType :: Sampler => self
573
- . tcx
574
- . dcx ( )
575
- . fatal ( "cannot create a constant sampler value" ) ,
576
- SpirvType :: SampledImage { .. } => self
577
- . tcx
578
- . dcx ( )
579
- . fatal ( "cannot create a constant sampled image value" ) ,
580
- SpirvType :: InterfaceBlock { .. } => self
581
- . tcx
582
- . dcx ( )
583
- . fatal ( "cannot create a constant interface block value" ) ,
584
- SpirvType :: AccelerationStructureKhr => self
585
- . tcx
586
- . dcx ( )
587
- . fatal ( "cannot create a constant acceleration structure" ) ,
588
- SpirvType :: RayQueryKhr => self . tcx . dcx ( ) . fatal ( "cannot create a constant ray query" ) ,
589
575
}
590
576
}
591
577
}
0 commit comments