Skip to content

Commit 1e33b18

Browse files
committed
Reduce SpirvValue lossiness around strip_ptrcasts and const_fold_load.
1 parent 03b639f commit 1e33b18

File tree

3 files changed

+120
-110
lines changed

3 files changed

+120
-110
lines changed

crates/rustc_codegen_spirv/src/builder/builder_methods.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2221,8 +2221,8 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
22212221
self.debug_type(dest_pointee),
22222222
);
22232223
// Defer the cast so that it has a chance to be avoided.
2224-
let original_ptr = ptr.def(self);
2225-
let bitcast_result_id = self.emit().bitcast(dest_ty, None, original_ptr).unwrap();
2224+
let ptr_id = ptr.def(self);
2225+
let bitcast_result_id = self.emit().bitcast(dest_ty, None, ptr_id).unwrap();
22262226

22272227
self.zombie(
22282228
bitcast_result_id,
@@ -2237,10 +2237,13 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
22372237

22382238
SpirvValue {
22392239
zombie_waiting_for_span: false,
2240-
kind: SpirvValueKind::LogicalPtrCast {
2241-
original_ptr,
2242-
original_ptr_ty: ptr.ty,
2243-
bitcast_result_id,
2240+
kind: SpirvValueKind::Def {
2241+
id: bitcast_result_id,
2242+
original_ptr_before_casts: Some(SpirvValue {
2243+
zombie_waiting_for_span: ptr.zombie_waiting_for_span,
2244+
kind: ptr_id,
2245+
ty: ptr.ty,
2246+
}),
22442247
},
22452248
ty: dest_ty,
22462249
}
@@ -2995,7 +2998,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
29952998
return_type,
29962999
arguments,
29973000
} => (
2998-
if let SpirvValueKind::FnAddr { function } = callee.kind {
3001+
if let SpirvValueKind::FnAddr { function, .. } = callee.kind {
29993002
assert_ty_eq!(self, callee_ty, pointee);
30003003
function
30013004
}
@@ -3132,11 +3135,11 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
31323135
// HACK(eddyb) some entry-points only take a `&str`, not `fmt::Arguments`.
31333136
if let [
31343137
SpirvValue {
3135-
kind: SpirvValueKind::Def(a_id),
3138+
kind: SpirvValueKind::Def { id: a_id, .. },
31363139
..
31373140
},
31383141
SpirvValue {
3139-
kind: SpirvValueKind::Def(b_id),
3142+
kind: SpirvValueKind::Def { id: b_id, .. },
31403143
..
31413144
},
31423145
ref other_args @ ..,
@@ -3155,14 +3158,20 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
31553158
// HACK(eddyb) `panic_nounwind_fmt` takes an extra argument.
31563159
[
31573160
SpirvValue {
3158-
kind: SpirvValueKind::Def(format_args_id),
3161+
kind:
3162+
SpirvValueKind::Def {
3163+
id: format_args_id, ..
3164+
},
31593165
..
31603166
},
31613167
_, // `&'static panic::Location<'static>`
31623168
]
31633169
| [
31643170
SpirvValue {
3165-
kind: SpirvValueKind::Def(format_args_id),
3171+
kind:
3172+
SpirvValueKind::Def {
3173+
id: format_args_id, ..
3174+
},
31663175
..
31673176
},
31683177
_, // `force_no_backtrace: bool`

crates/rustc_codegen_spirv/src/builder_spirv.rs

Lines changed: 96 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use rustc_abi::Size;
1616
use rustc_arena::DroplessArena;
1717
use rustc_codegen_ssa::traits::ConstCodegenMethods as _;
1818
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
19-
use rustc_middle::bug;
2019
use rustc_middle::mir::interpret::ConstAllocation;
2120
use rustc_middle::ty::TyCtxt;
2221
use rustc_span::source_map::SourceMap;
@@ -31,84 +30,86 @@ use std::str;
3130
use std::sync::Arc;
3231
use 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)]
3536
pub 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+
7990
impl 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

Comments
 (0)