@@ -30,6 +30,7 @@ use std::borrow::Cow;
30
30
use std:: cell:: Cell ;
31
31
use std:: convert:: TryInto ;
32
32
use std:: iter:: { self , empty} ;
33
+ use std:: ops:: RangeInclusive ;
33
34
34
35
macro_rules! simple_op {
35
36
(
@@ -412,9 +413,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
412
413
// FIXME(eddyb) this isn't efficient, `recover_access_chain_from_offset`
413
414
// could instead be doing all the extra digging itself.
414
415
let mut indices = SmallVec :: < [ _ ; 8 ] > :: new ( ) ;
415
- while let Some ( ( inner_indices, inner_ty) ) =
416
- self . recover_access_chain_from_offset ( leaf_ty, Size :: ZERO , Some ( size) , None )
417
- {
416
+ while let Some ( ( inner_indices, inner_ty) ) = self . recover_access_chain_from_offset (
417
+ leaf_ty,
418
+ Size :: ZERO ,
419
+ Some ( size) ..=Some ( size) ,
420
+ None ,
421
+ ) {
418
422
indices. extend ( inner_indices) ;
419
423
leaf_ty = inner_ty;
420
424
}
@@ -439,8 +443,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
439
443
}
440
444
441
445
/// If possible, return the appropriate `OpAccessChain` indices for going
442
- /// from a pointer to `ty`, to a pointer to some leaf field/element of size
443
- /// `leaf_size` (and optionally type `leaf_ty`), while adding `offset` bytes.
446
+ /// from a pointer to `ty`, to a pointer to some leaf field/element having
447
+ /// a size that fits `leaf_size_range` (and, optionally, the type `leaf_ty`),
448
+ /// while adding `offset` bytes.
444
449
///
445
450
/// That is, try to turn `((_: *T) as *u8).add(offset) as *Leaf` into a series
446
451
/// of struct field and array/vector element accesses.
@@ -449,7 +454,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
449
454
mut ty : <Self as BackendTypes >:: Type ,
450
455
mut offset : Size ,
451
456
// FIXME(eddyb) using `None` for "unsized" is a pretty bad design.
452
- leaf_size_or_unsized : Option < Size > ,
457
+ leaf_size_or_unsized_range : RangeInclusive < Option < Size > > ,
453
458
leaf_ty : Option < <Self as BackendTypes >:: Type > ,
454
459
) -> Option < ( SmallVec < [ u32 ; 8 ] > , <Self as BackendTypes >:: Type ) > {
455
460
assert_ne ! ( Some ( ty) , leaf_ty) ;
@@ -460,7 +465,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
460
465
Sized ( Size ) ,
461
466
Unsized ,
462
467
}
463
- let leaf_size = leaf_size_or_unsized. map_or ( MaybeSized :: Unsized , MaybeSized :: Sized ) ;
468
+ let leaf_size_range = {
469
+ let r = leaf_size_or_unsized_range;
470
+ let [ start, end] =
471
+ [ r. start ( ) , r. end ( ) ] . map ( |x| x. map_or ( MaybeSized :: Unsized , MaybeSized :: Sized ) ) ;
472
+ start..=end
473
+ } ;
464
474
465
475
// NOTE(eddyb) `ty` and `ty_kind`/`ty_size` should be kept in sync.
466
476
let mut ty_kind = self . lookup_type ( ty) ;
@@ -493,7 +503,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
493
503
if MaybeSized :: Sized ( offset_in_field) < field_ty_size
494
504
// If the field is a zero sized type, check the
495
505
// expected size and type to get the correct entry
496
- || offset_in_field == Size :: ZERO && leaf_size == MaybeSized :: Sized ( Size :: ZERO ) && leaf_ty == Some ( field_ty)
506
+ || offset_in_field == Size :: ZERO
507
+ && leaf_size_range. contains ( & MaybeSized :: Sized ( Size :: ZERO ) ) && leaf_ty == Some ( field_ty)
497
508
{
498
509
Some ( ( i, field_ty, field_ty_kind, field_ty_size, offset_in_field) )
499
510
} else {
@@ -525,19 +536,211 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
525
536
}
526
537
527
538
// Avoid digging beyond the point the leaf could actually fit.
528
- if ty_size < leaf_size {
539
+ if ty_size < * leaf_size_range . start ( ) {
529
540
return None ;
530
541
}
531
542
532
543
if offset == Size :: ZERO
533
- && ty_size == leaf_size
544
+ && leaf_size_range . contains ( & ty_size)
534
545
&& leaf_ty. map_or ( true , |leaf_ty| leaf_ty == ty)
535
546
{
536
547
return Some ( ( indices, ty) ) ;
537
548
}
538
549
}
539
550
}
540
551
552
+ fn maybe_inbounds_gep (
553
+ & mut self ,
554
+ ty : Word ,
555
+ ptr : SpirvValue ,
556
+ combined_indices : & [ SpirvValue ] ,
557
+ is_inbounds : bool ,
558
+ ) -> SpirvValue {
559
+ let ( & ptr_base_index, indices) = combined_indices. split_first ( ) . unwrap ( ) ;
560
+
561
+ // The first index is an offset to the pointer, the rest are actual members.
562
+ // https://llvm.org/docs/GetElementPtr.html
563
+ // "An OpAccessChain instruction is the equivalent of an LLVM getelementptr instruction where the first index element is zero."
564
+ // https://github.com/gpuweb/gpuweb/issues/33
565
+ let mut result_pointee_type = ty;
566
+ let indices: Vec < _ > = indices
567
+ . iter ( )
568
+ . map ( |index| {
569
+ result_pointee_type = match self . lookup_type ( result_pointee_type) {
570
+ SpirvType :: Array { element, .. } | SpirvType :: RuntimeArray { element } => {
571
+ element
572
+ }
573
+ _ => self . fatal ( format ! (
574
+ "GEP not implemented for type {}" ,
575
+ self . debug_type( result_pointee_type)
576
+ ) ) ,
577
+ } ;
578
+ index. def ( self )
579
+ } )
580
+ . collect ( ) ;
581
+
582
+ // Special-case field accesses through a `pointercast`, to accesss the
583
+ // right field in the original type, for the `Logical` addressing model.
584
+ let ptr = ptr. strip_ptrcasts ( ) ;
585
+ let ptr_id = ptr. def ( self ) ;
586
+ let original_pointee_ty = match self . lookup_type ( ptr. ty ) {
587
+ SpirvType :: Pointer { pointee } => pointee,
588
+ other => self . fatal ( format ! ( "gep called on non-pointer type: {other:?}" ) ) ,
589
+ } ;
590
+
591
+ // HACK(eddyb) `struct_gep` itself is falling out of use, as it's being
592
+ // replaced upstream by `ptr_add` (aka `inbounds_gep` with byte offsets).
593
+ //
594
+ // FIXME(eddyb) get rid of everything other than:
595
+ // - constant byte offset (`ptr_add`?)
596
+ // - dynamic indexing of a single array
597
+ let const_ptr_offset = self
598
+ . builder
599
+ . lookup_const_u64 ( ptr_base_index)
600
+ . and_then ( |idx| Some ( idx * self . lookup_type ( ty) . sizeof ( self ) ?) ) ;
601
+ if let Some ( const_ptr_offset) = const_ptr_offset {
602
+ if let Some ( ( base_indices, base_pointee_ty) ) = self . recover_access_chain_from_offset (
603
+ original_pointee_ty,
604
+ const_ptr_offset,
605
+ Some ( Size :: ZERO ) ..=None ,
606
+ None ,
607
+ ) {
608
+ // FIXME(eddyb) this condition is pretty limiting, but
609
+ // eventually it shouldn't matter if GEPs are going away.
610
+ if ty == base_pointee_ty || indices. is_empty ( ) {
611
+ let result_pointee_type = if indices. is_empty ( ) {
612
+ base_pointee_ty
613
+ } else {
614
+ result_pointee_type
615
+ } ;
616
+ let indices = base_indices
617
+ . into_iter ( )
618
+ . map ( |idx| self . constant_u32 ( self . span ( ) , idx) . def ( self ) )
619
+ . chain ( indices)
620
+ . collect ( ) ;
621
+ return self . emit_access_chain (
622
+ self . type_ptr_to ( result_pointee_type) ,
623
+ ptr_id,
624
+ None ,
625
+ indices,
626
+ is_inbounds,
627
+ ) ;
628
+ }
629
+ }
630
+ }
631
+
632
+ let result_type = self . type_ptr_to ( result_pointee_type) ;
633
+
634
+ // Check if `ptr_id` is defined by an `OpAccessChain`, and if it is,
635
+ // grab its base pointer and indices.
636
+ //
637
+ // FIXME(eddyb) this could get ridiculously expensive, at the very least
638
+ // it could use `.rev()`, hoping the base pointer was recently defined?
639
+ let maybe_original_access_chain = if ty == original_pointee_ty {
640
+ let emit = self . emit ( ) ;
641
+ let module = emit. module_ref ( ) ;
642
+ let func = & module. functions [ emit. selected_function ( ) . unwrap ( ) ] ;
643
+ let base_ptr_and_combined_indices = func
644
+ . all_inst_iter ( )
645
+ . find ( |inst| inst. result_id == Some ( ptr_id) )
646
+ . and_then ( |ptr_def_inst| {
647
+ if matches ! (
648
+ ptr_def_inst. class. opcode,
649
+ Op :: AccessChain | Op :: InBoundsAccessChain
650
+ ) {
651
+ let base_ptr = ptr_def_inst. operands [ 0 ] . unwrap_id_ref ( ) ;
652
+ let indices = ptr_def_inst. operands [ 1 ..]
653
+ . iter ( )
654
+ . map ( |op| op. unwrap_id_ref ( ) )
655
+ . collect :: < Vec < _ > > ( ) ;
656
+ Some ( ( base_ptr, indices) )
657
+ } else {
658
+ None
659
+ }
660
+ } ) ;
661
+ base_ptr_and_combined_indices
662
+ } else {
663
+ None
664
+ } ;
665
+ if let Some ( ( original_ptr, mut original_indices) ) = maybe_original_access_chain {
666
+ // Transform the following:
667
+ // OpAccessChain original_ptr [a, b, c]
668
+ // OpPtrAccessChain ptr base [d, e, f]
669
+ // into
670
+ // OpAccessChain original_ptr [a, b, c + base, d, e, f]
671
+ // to remove the need for OpPtrAccessChain
672
+ let last = original_indices. last_mut ( ) . unwrap ( ) ;
673
+ * last = self
674
+ . add ( last. with_type ( ptr_base_index. ty ) , ptr_base_index)
675
+ . def ( self ) ;
676
+ original_indices. extend ( indices) ;
677
+ return self . emit_access_chain (
678
+ result_type,
679
+ original_ptr,
680
+ None ,
681
+ original_indices,
682
+ is_inbounds,
683
+ ) ;
684
+ }
685
+
686
+ // HACK(eddyb) temporary workaround for untyped pointers upstream.
687
+ // FIXME(eddyb) replace with untyped memory SPIR-V + `qptr` or similar.
688
+ let ptr = self . pointercast ( ptr, self . type_ptr_to ( ty) ) ;
689
+ let ptr_id = ptr. def ( self ) ;
690
+
691
+ self . emit_access_chain (
692
+ result_type,
693
+ ptr_id,
694
+ Some ( ptr_base_index) ,
695
+ indices,
696
+ is_inbounds,
697
+ )
698
+ }
699
+
700
+ fn emit_access_chain (
701
+ & self ,
702
+ result_type : <Self as BackendTypes >:: Type ,
703
+ pointer : Word ,
704
+ ptr_base_index : Option < SpirvValue > ,
705
+ indices : Vec < Word > ,
706
+ is_inbounds : bool ,
707
+ ) -> SpirvValue {
708
+ let mut emit = self . emit ( ) ;
709
+
710
+ let non_zero_ptr_base_index =
711
+ ptr_base_index. filter ( |& idx| self . builder . lookup_const_u64 ( idx) != Some ( 0 ) ) ;
712
+ if let Some ( ptr_base_index) = non_zero_ptr_base_index {
713
+ let result = if is_inbounds {
714
+ emit. in_bounds_ptr_access_chain (
715
+ result_type,
716
+ None ,
717
+ pointer,
718
+ ptr_base_index. def ( self ) ,
719
+ indices,
720
+ )
721
+ } else {
722
+ emit. ptr_access_chain (
723
+ result_type,
724
+ None ,
725
+ pointer,
726
+ ptr_base_index. def ( self ) ,
727
+ indices,
728
+ )
729
+ }
730
+ . unwrap ( ) ;
731
+ self . zombie ( result, "cannot offset a pointer to an arbitrary element" ) ;
732
+ result
733
+ } else {
734
+ if is_inbounds {
735
+ emit. in_bounds_access_chain ( result_type, None , pointer, indices)
736
+ } else {
737
+ emit. access_chain ( result_type, None , pointer, indices)
738
+ }
739
+ . unwrap ( )
740
+ }
741
+ . with_type ( result_type)
742
+ }
743
+
541
744
fn fptoint_sat (
542
745
& mut self ,
543
746
signed : bool ,
@@ -1361,7 +1564,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
1361
1564
}
1362
1565
1363
1566
fn gep ( & mut self , ty : Self :: Type , ptr : Self :: Value , indices : & [ Self :: Value ] ) -> Self :: Value {
1364
- self . gep_help ( ty, ptr, indices, false )
1567
+ self . maybe_inbounds_gep ( ty, ptr, indices, false )
1365
1568
}
1366
1569
1367
1570
fn inbounds_gep (
@@ -1370,7 +1573,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
1370
1573
ptr : Self :: Value ,
1371
1574
indices : & [ Self :: Value ] ,
1372
1575
) -> Self :: Value {
1373
- self . gep_help ( ty, ptr, indices, true )
1576
+ self . maybe_inbounds_gep ( ty, ptr, indices, true )
1374
1577
}
1375
1578
1376
1579
fn struct_gep ( & mut self , ty : Self :: Type , ptr : Self :: Value , idx : u64 ) -> Self :: Value {
@@ -1395,6 +1598,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
1395
1598
"struct_gep not on struct, array, or vector type: {other:?}, index {idx}"
1396
1599
) ) ,
1397
1600
} ;
1601
+ let result_pointee_size = self . lookup_type ( result_pointee_type) . sizeof ( self ) ;
1398
1602
let result_type = self . type_ptr_to ( result_pointee_type) ;
1399
1603
1400
1604
// Special-case field accesses through a `pointercast`, to accesss the
@@ -1407,7 +1611,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
1407
1611
if let Some ( ( indices, _) ) = self . recover_access_chain_from_offset (
1408
1612
original_pointee_ty,
1409
1613
offset,
1410
- self . lookup_type ( result_pointee_type ) . sizeof ( self ) ,
1614
+ result_pointee_size..=result_pointee_size ,
1411
1615
Some ( result_pointee_type) ,
1412
1616
) {
1413
1617
let original_ptr = ptr. def ( self ) ;
@@ -1586,9 +1790,12 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
1586
1790
// FIXME(eddyb) this isn't efficient, `recover_access_chain_from_offset`
1587
1791
// could instead be doing all the extra digging itself.
1588
1792
let mut indices = SmallVec :: < [ _ ; 8 ] > :: new ( ) ;
1589
- while let Some ( ( inner_indices, inner_ty) ) =
1590
- self . recover_access_chain_from_offset ( leaf_ty, Size :: ZERO , Some ( size) , None )
1591
- {
1793
+ while let Some ( ( inner_indices, inner_ty) ) = self . recover_access_chain_from_offset (
1794
+ leaf_ty,
1795
+ Size :: ZERO ,
1796
+ Some ( size) ..=Some ( size) ,
1797
+ None ,
1798
+ ) {
1592
1799
indices. extend ( inner_indices) ;
1593
1800
leaf_ty = inner_ty;
1594
1801
}
@@ -1716,9 +1923,17 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
1716
1923
return self . const_bitcast ( ptr, dest_ty) ;
1717
1924
}
1718
1925
1926
+ if ptr. ty == dest_ty {
1927
+ return ptr;
1928
+ }
1929
+
1719
1930
// Strip a previous `pointercast`, to reveal the original pointer type.
1720
1931
let ptr = ptr. strip_ptrcasts ( ) ;
1721
1932
1933
+ if ptr. ty == dest_ty {
1934
+ return ptr;
1935
+ }
1936
+
1722
1937
let ptr_pointee = match self . lookup_type ( ptr. ty ) {
1723
1938
SpirvType :: Pointer { pointee } => pointee,
1724
1939
other => self . fatal ( format ! (
@@ -1731,12 +1946,12 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
1731
1946
"pointercast called on non-pointer dest type: {other:?}"
1732
1947
) ) ,
1733
1948
} ;
1734
- if ptr . ty == dest_ty {
1735
- ptr
1736
- } else if let Some ( ( indices, _) ) = self . recover_access_chain_from_offset (
1949
+ let dest_pointee_size = self . lookup_type ( dest_pointee ) . sizeof ( self ) ;
1950
+
1951
+ if let Some ( ( indices, _) ) = self . recover_access_chain_from_offset (
1737
1952
ptr_pointee,
1738
1953
Size :: ZERO ,
1739
- self . lookup_type ( dest_pointee ) . sizeof ( self ) ,
1954
+ dest_pointee_size..=dest_pointee_size ,
1740
1955
Some ( dest_pointee) ,
1741
1956
) {
1742
1957
let indices = indices
@@ -2687,6 +2902,13 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2687
2902
Store ( ID , ID ) ,
2688
2903
Load ( ID , ID ) ,
2689
2904
Call ( ID , ID , SmallVec < [ ID ; 4 ] > ) ,
2905
+
2906
+ // HACK(eddyb) this only exists for better error reporting,
2907
+ // as `Result<Inst<...>, Op>` would only report one `Op`.
2908
+ Unsupported (
2909
+ // HACK(eddyb) only exists for `fmt::Debug` in case of error.
2910
+ #[ allow( dead_code) ] Op ,
2911
+ ) ,
2690
2912
}
2691
2913
2692
2914
let taken_inst_idx_range = Cell :: new ( func. blocks [ block_idx] . instructions . len ( ) ) ..;
@@ -2732,7 +2954,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2732
2954
( Op :: FunctionCall , Some ( r) , [ f, args @ ..] ) => {
2733
2955
Inst :: Call ( r, * f, args. iter ( ) . copied ( ) . collect ( ) )
2734
2956
}
2735
- _ => return None ,
2957
+ _ => Inst :: Unsupported ( inst . class . opcode ) ,
2736
2958
} ,
2737
2959
)
2738
2960
} ) ;
0 commit comments