@@ -16,7 +16,6 @@ use rustc_abi::Size;
1616use rustc_arena:: DroplessArena ;
1717use rustc_codegen_ssa:: traits:: ConstCodegenMethods as _;
1818use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
19- use rustc_middle:: bug;
2019use rustc_middle:: mir:: interpret:: ConstAllocation ;
2120use rustc_middle:: ty:: TyCtxt ;
2221use rustc_span:: source_map:: SourceMap ;
@@ -31,84 +30,86 @@ use std::str;
3130use std:: sync:: Arc ;
3231use std:: { fs:: File , io:: Write , path:: Path } ;
3332
33+ // HACK(eddyb) silence warnings that are inaccurate wrt future changes.
34+ #[ non_exhaustive]
3435#[ derive( Copy , Clone , Debug , Ord , PartialOrd , Eq , PartialEq , Hash ) ]
3536pub enum SpirvValueKind {
36- Def ( Word ) ,
37+ Def {
38+ id : Word ,
39+
40+ /// If `id` is a pointer cast, this will be `Some`, and contain all the
41+ /// information necessary to regenerate the original `SpirvValue` before
42+ /// *any* pointer casts were applied, effectively deferring the casts
43+ /// (as long as all downstream uses apply `.strip_ptrcasts()` first),
44+ /// and bypassing errors they might cause (due to SPIR-V limitations).
45+ //
46+ // FIXME(eddyb) wouldn't it be easier to use this for *any* bitcasts?
47+ // (with some caveats around dedicated int<->ptr casts vs bitcasts)
48+ original_ptr_before_casts : Option < SpirvValue < Word > > ,
49+ } ,
3750
3851 // FIXME(eddyb) this shouldn't be needed, but `rustc_codegen_ssa` still relies
3952 // on converting `Function`s to `Value`s even for direct calls, the `Builder`
4053 // should just have direct and indirect `call` variants (or a `Callee` enum).
4154 FnAddr {
4255 function : Word ,
43- } ,
44-
45- /// Deferred pointer cast, for the `Logical` addressing model (which doesn't
46- /// really support raw pointers in the way Rust expects to be able to use).
47- ///
48- /// The cast's target pointer type is the `ty` of the `SpirvValue` that has
49- /// `LogicalPtrCast` as its `kind`, as it would be redundant to have it here.
50- LogicalPtrCast {
51- /// Pointer value being cast.
52- original_ptr : Word ,
5356
54- /// Pointer type of `original_ptr`.
55- original_ptr_ty : Word ,
56-
57- /// Result ID for the `OpBitcast` instruction representing the cast,
58- /// to attach zombies to.
59- //
60- // HACK(eddyb) having an `OpBitcast` only works by being DCE'd away,
61- // or by being replaced with a noop in `qptr::lower`.
62- bitcast_result_id : Word ,
57+ // FIXME(eddyb) replace this ad-hoc zombie with a proper `SpirvConst`.
58+ zombie_id : Word ,
6359 } ,
6460}
6561
6662#[ derive( Copy , Clone , Debug , Ord , PartialOrd , Eq , PartialEq , Hash ) ]
67- pub struct SpirvValue {
63+ pub struct SpirvValue < K = SpirvValueKind > {
6864 // HACK(eddyb) used to cheaply check whether this is a SPIR-V value ID
6965 // with a "zombie" (deferred error) attached to it, that may need a `Span`
7066 // still (e.g. such as constants, which can't easily take a `Span`).
7167 // FIXME(eddyb) a whole `bool` field is sadly inefficient, but anything
7268 // which may make `SpirvValue` smaller requires far too much impl effort.
7369 pub zombie_waiting_for_span : bool ,
7470
75- pub kind : SpirvValueKind ,
71+ pub kind : K ,
7672 pub ty : Word ,
7773}
7874
75+ impl < K > SpirvValue < K > {
76+ fn map_kind < K2 > ( self , f : impl FnOnce ( K ) -> K2 ) -> SpirvValue < K2 > {
77+ let SpirvValue {
78+ zombie_waiting_for_span,
79+ kind,
80+ ty,
81+ } = self ;
82+ SpirvValue {
83+ zombie_waiting_for_span,
84+ kind : f ( kind) ,
85+ ty,
86+ }
87+ }
88+ }
89+
7990impl SpirvValue {
8091 pub fn strip_ptrcasts ( self ) -> Self {
8192 match self . kind {
82- SpirvValueKind :: LogicalPtrCast {
83- original_ptr,
84- original_ptr_ty,
85- bitcast_result_id : _,
86- } => original_ptr. with_type ( original_ptr_ty) ,
93+ SpirvValueKind :: Def {
94+ id : _,
95+ original_ptr_before_casts : Some ( original_ptr) ,
96+ } => original_ptr. map_kind ( |id| SpirvValueKind :: Def {
97+ id,
98+ original_ptr_before_casts : None ,
99+ } ) ,
87100
88101 _ => self ,
89102 }
90103 }
91104
92105 pub fn const_fold_load ( self , cx : & CodegenCx < ' _ > ) -> Option < Self > {
93- match self . kind {
94- SpirvValueKind :: Def ( id) => {
95- let & entry = cx. builder . id_to_const . borrow ( ) . get ( & id) ?;
96- match entry. val {
97- SpirvConst :: PtrTo { pointee } => {
98- let ty = match cx. lookup_type ( self . ty ) {
99- SpirvType :: Pointer { pointee } => pointee,
100- ty => bug ! ( "load called on value that wasn't a pointer: {:?}" , ty) ,
101- } ;
102- Some ( SpirvValue {
103- zombie_waiting_for_span : entry. legal . is_err ( ) ,
104- kind : SpirvValueKind :: Def ( pointee) ,
105- ty,
106- } )
107- }
108- _ => None ,
109- }
106+ match cx. builder . lookup_const ( self ) ? {
107+ SpirvConst :: PtrTo { pointee } => {
108+ // HACK(eddyb) this obtains a `SpirvValue` from the ID it contains,
109+ // so there's some conceptual inefficiency there, but it does
110+ // prevent any of the other details from being lost accidentally.
111+ Some ( cx. builder . id_to_const_and_val . borrow ( ) . get ( & pointee) ?. val . 1 )
110112 }
111-
112113 _ => None ,
113114 }
114115 }
@@ -128,24 +129,7 @@ impl SpirvValue {
128129
129130 pub fn def_with_span ( self , cx : & CodegenCx < ' _ > , span : Span ) -> Word {
130131 let id = match self . kind {
131- SpirvValueKind :: FnAddr { .. } => {
132- cx. builder
133- . const_to_id
134- . borrow ( )
135- . get ( & WithType {
136- ty : self . ty ,
137- val : SpirvConst :: ZombieUndefForFnAddr ,
138- } )
139- . expect ( "FnAddr didn't go through proper undef registration" )
140- . val
141- }
142-
143- SpirvValueKind :: Def ( id)
144- | SpirvValueKind :: LogicalPtrCast {
145- original_ptr : _,
146- original_ptr_ty : _,
147- bitcast_result_id : id,
148- } => id,
132+ SpirvValueKind :: Def { id, .. } | SpirvValueKind :: FnAddr { zombie_id : id, .. } => id,
149133 } ;
150134 if self . zombie_waiting_for_span {
151135 cx. add_span_to_zombie_if_missing ( id, span) ;
@@ -162,7 +146,10 @@ impl SpirvValueExt for Word {
162146 fn with_type ( self , ty : Word ) -> SpirvValue {
163147 SpirvValue {
164148 zombie_waiting_for_span : false ,
165- kind : SpirvValueKind :: Def ( self ) ,
149+ kind : SpirvValueKind :: Def {
150+ id : self ,
151+ original_ptr_before_casts : None ,
152+ } ,
166153 ty,
167154 }
168155 }
@@ -380,11 +367,12 @@ pub struct BuilderSpirv<'tcx> {
380367 builder : RefCell < Builder > ,
381368
382369 // Bidirectional maps between `SpirvConst` and the ID of the defined global
383- // (e.g. `OpConstant...`) instruction.
384- // NOTE(eddyb) both maps have `WithConstLegality` around their keys, which
385- // allows getting that legality information without additional lookups.
386- const_to_id : RefCell < FxHashMap < WithType < SpirvConst < ' tcx , ' tcx > > , WithConstLegality < Word > > > ,
387- id_to_const : RefCell < FxHashMap < Word , WithConstLegality < SpirvConst < ' tcx , ' tcx > > > > ,
370+ // (e.g. `OpConstant...`) instruction, with additional information in values
371+ // (i.e. each map is keyed by only some part of the other map's value type),
372+ // as needed to streamline operations (e.g. avoiding rederiving `SpirvValue`).
373+ const_to_val : RefCell < FxHashMap < WithType < SpirvConst < ' tcx , ' tcx > > , SpirvValue > > ,
374+ id_to_const_and_val :
375+ RefCell < FxHashMap < Word , WithConstLegality < ( SpirvConst < ' tcx , ' tcx > , SpirvValue ) > > > ,
388376
389377 debug_file_cache : RefCell < FxHashMap < DebugFileKey , DebugFileSpirv < ' tcx > > > ,
390378
@@ -455,8 +443,8 @@ impl<'tcx> BuilderSpirv<'tcx> {
455443 source_map : tcx. sess . source_map ( ) ,
456444 dropless_arena : & tcx. arena . dropless ,
457445 builder : RefCell :: new ( builder) ,
458- const_to_id : Default :: default ( ) ,
459- id_to_const : Default :: default ( ) ,
446+ const_to_val : Default :: default ( ) ,
447+ id_to_const_and_val : Default :: default ( ) ,
460448 debug_file_cache : Default :: default ( ) ,
461449 enabled_capabilities,
462450 }
@@ -560,12 +548,8 @@ impl<'tcx> BuilderSpirv<'tcx> {
560548 } ;
561549
562550 let val_with_type = WithType { ty, val } ;
563- if let Some ( entry) = self . const_to_id . borrow ( ) . get ( & val_with_type) {
564- return SpirvValue {
565- zombie_waiting_for_span : entry. legal . is_err ( ) ,
566- kind : SpirvValueKind :: Def ( entry. val ) ,
567- ty,
568- } ;
551+ if let Some ( & v) = self . const_to_val . borrow ( ) . get ( & val_with_type) {
552+ return v;
569553 }
570554 let val = val_with_type. val ;
571555
@@ -697,11 +681,11 @@ impl<'tcx> BuilderSpirv<'tcx> {
697681 SpirvConst :: Composite ( v) => v
698682 . iter ( )
699683 . map ( |field| {
700- let field_entry = & self . id_to_const . borrow ( ) [ field] ;
684+ let field_entry = & self . id_to_const_and_val . borrow ( ) [ field] ;
701685 field_entry. legal . and (
702686 // `field` is itself some legal `SpirvConst`, but can we have
703687 // it as part of an `OpConstantComposite`?
704- match field_entry. val {
688+ match field_entry. val . 0 {
705689 SpirvConst :: PtrTo { .. } => Err ( IllegalConst :: Shallow (
706690 LeafIllegalConst :: CompositeContainsPtrTo ,
707691 ) ) ,
@@ -729,14 +713,16 @@ impl<'tcx> BuilderSpirv<'tcx> {
729713 } )
730714 . unwrap_or ( Ok ( ( ) ) ) ,
731715
732- SpirvConst :: PtrTo { pointee } => match self . id_to_const . borrow ( ) [ & pointee] . legal {
733- Ok ( ( ) ) => Ok ( ( ) ) ,
716+ SpirvConst :: PtrTo { pointee } => {
717+ match self . id_to_const_and_val . borrow ( ) [ & pointee] . legal {
718+ Ok ( ( ) ) => Ok ( ( ) ) ,
734719
735- // `Shallow` becomes `Indirect` when placed behind a pointer.
736- Err ( IllegalConst :: Shallow ( cause) | IllegalConst :: Indirect ( cause) ) => {
737- Err ( IllegalConst :: Indirect ( cause) )
720+ // `Shallow` becomes `Indirect` when placed behind a pointer.
721+ Err ( IllegalConst :: Shallow ( cause) | IllegalConst :: Indirect ( cause) ) => {
722+ Err ( IllegalConst :: Indirect ( cause) )
723+ }
738724 }
739- } ,
725+ }
740726
741727 SpirvConst :: ConstDataFromAlloc ( _) => Err ( IllegalConst :: Shallow (
742728 LeafIllegalConst :: UntypedConstDataFromAlloc ,
@@ -754,32 +740,44 @@ impl<'tcx> BuilderSpirv<'tcx> {
754740 }
755741
756742 let val = val. tcx_arena_alloc_slices ( cx) ;
743+
744+ // FIXME(eddyb) the `val`/`v` name clash is a bit unfortunate.
745+ let v = SpirvValue {
746+ zombie_waiting_for_span : legal. is_err ( ) ,
747+ kind : SpirvValueKind :: Def {
748+ id,
749+ original_ptr_before_casts : None ,
750+ } ,
751+ ty,
752+ } ;
753+
757754 assert_matches ! (
758- self . const_to_id
755+ self . const_to_val
759756 . borrow_mut( )
760- . insert( WithType { ty, val } , WithConstLegality { val : id , legal } ) ,
757+ . insert( WithType { ty, val } , v ) ,
761758 None
762759 ) ;
763760 assert_matches ! (
764- self . id_to_const
765- . borrow_mut( )
766- . insert( id, WithConstLegality { val, legal } ) ,
761+ self . id_to_const_and_val. borrow_mut( ) . insert(
762+ id,
763+ WithConstLegality {
764+ val: ( val, v) ,
765+ legal
766+ }
767+ ) ,
767768 None
768769 ) ;
769- SpirvValue {
770- zombie_waiting_for_span : legal. is_err ( ) ,
771- kind : SpirvValueKind :: Def ( id) ,
772- ty,
773- }
770+
771+ v
774772 }
775773
776774 pub fn lookup_const_by_id ( & self , id : Word ) -> Option < SpirvConst < ' tcx , ' tcx > > {
777- Some ( self . id_to_const . borrow ( ) . get ( & id) ?. val )
775+ Some ( self . id_to_const_and_val . borrow ( ) . get ( & id) ?. val . 0 )
778776 }
779777
780778 pub fn lookup_const ( & self , def : SpirvValue ) -> Option < SpirvConst < ' tcx , ' tcx > > {
781779 match def. kind {
782- SpirvValueKind :: Def ( id ) => self . lookup_const_by_id ( id) ,
780+ SpirvValueKind :: Def { id , .. } => self . lookup_const_by_id ( id) ,
783781 _ => None ,
784782 }
785783 }
0 commit comments