rustc_codegen_spirv/builder/
mod.rs

1mod builder_methods;
2mod byte_addressable_buffer;
3mod ext_inst;
4mod format_args_decompiler;
5mod intrinsics;
6pub mod libm_intrinsics;
7mod spirv_asm;
8
9pub use ext_inst::ExtInst;
10use rustc_span::DUMMY_SP;
11pub use spirv_asm::InstructionTable;
12
13// HACK(eddyb) avoids rewriting all of the imports (see `lib.rs` and `build.rs`).
14use crate::maybe_pqp_cg_ssa as rustc_codegen_ssa;
15
16use crate::builder_spirv::{SpirvValue, SpirvValueExt};
17use crate::codegen_cx::CodegenCx;
18use crate::spirv_type::SpirvType;
19use rspirv::spirv::Word;
20use rustc_abi::{HasDataLayout, Size, TargetDataLayout};
21use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
22use rustc_codegen_ssa::mir::place::PlaceRef;
23use rustc_codegen_ssa::traits::{
24    AbiBuilderMethods, ArgAbiBuilderMethods, BackendTypes, BuilderMethods,
25    CoverageInfoBuilderMethods, DebugInfoBuilderMethods, StaticBuilderMethods,
26    TypeMembershipCodegenMethods,
27};
28use rustc_errors::{Diag, DiagMessage};
29use rustc_middle::mir::coverage::CoverageKind;
30use rustc_middle::span_bug;
31use rustc_middle::ty::layout::{
32    FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError,
33    LayoutOfHelpers, TyAndLayout,
34};
35use rustc_middle::ty::{Instance, Ty, TyCtxt, TypingEnv};
36use rustc_span::Span;
37use rustc_span::def_id::DefId;
38use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
39use rustc_target::spec::{HasTargetSpec, Target};
40use std::ops::{Deref, Range};
41
42pub struct Builder<'a, 'tcx> {
43    cx: &'a CodegenCx<'tcx>,
44    current_block: <Self as BackendTypes>::BasicBlock,
45    current_span: Option<Span>,
46}
47
48impl<'a, 'tcx> Builder<'a, 'tcx> {
49    /// See comment on `BuilderCursor`
50    //
51    // FIXME(eddyb) take advantage of `&mut self` to avoid `RefCell` entirely
52    // (sadly it requires making `&CodegeCx`'s types/consts more like SPIR-T,
53    // and completely disjoint from mutably building functions).
54    pub fn emit(&mut self) -> std::cell::RefMut<'a, rspirv::dr::Builder> {
55        self.cx.builder.builder_for_block(self.current_block)
56    }
57
58    pub fn zombie(&self, word: Word, reason: &str) {
59        if let Some(current_span) = self.current_span {
60            self.zombie_with_span(word, current_span, reason);
61        } else {
62            self.zombie_no_span(word, reason);
63        }
64    }
65
66    pub fn undef_zombie(&self, word: Word, reason: &str) -> SpirvValue {
67        if let Some(current_span) = self.current_span {
68            self.undef_zombie_with_span(word, current_span, reason)
69        } else {
70            self.undef_zombie_no_span(word, reason)
71        }
72    }
73    pub fn undef_zombie_with_span(&self, ty: Word, span: Span, reason: &str) -> SpirvValue {
74        let undef = self.undef(ty);
75        self.zombie_with_span(undef.def(self), span, reason);
76        undef
77    }
78    pub fn undef_zombie_no_span(&self, ty: Word, reason: &str) -> SpirvValue {
79        let undef = self.undef(ty);
80        self.zombie_no_span(undef.def(self), reason);
81        undef
82    }
83
84    pub fn validate_atomic(&self, ty: Word, to_zombie: Word) {
85        if !self.i8_i16_atomics_allowed {
86            match self.lookup_type(ty) {
87                SpirvType::Integer(width, _) if width < 32 => {
88                    self.zombie(to_zombie, "atomic on i8 or i16 when disallowed by runtime");
89                }
90                _ => (),
91            }
92        }
93    }
94
95    #[track_caller]
96    pub fn struct_err(&self, msg: impl Into<DiagMessage>) -> Diag<'_> {
97        if let Some(current_span) = self.current_span {
98            self.tcx.dcx().struct_span_err(current_span, msg)
99        } else {
100            self.tcx.dcx().struct_err(msg)
101        }
102    }
103
104    #[track_caller]
105    pub fn err(&self, msg: impl Into<DiagMessage>) {
106        if let Some(current_span) = self.current_span {
107            self.tcx.dcx().span_err(current_span, msg);
108        } else {
109            self.tcx.dcx().err(msg);
110        }
111    }
112
113    #[track_caller]
114    pub fn fatal(&self, msg: impl Into<DiagMessage>) -> ! {
115        if let Some(current_span) = self.current_span {
116            self.tcx.dcx().span_fatal(current_span, msg)
117        } else {
118            self.tcx.dcx().fatal(msg)
119        }
120    }
121
122    pub fn span(&self) -> Span {
123        self.current_span.unwrap_or(DUMMY_SP)
124    }
125
126    // HACK(eddyb) like the `CodegenCx` method but with `self.span()` awareness.
127    pub fn type_ptr_to(&self, ty: Word) -> Word {
128        SpirvType::Pointer { pointee: ty }.def(self.span(), self)
129    }
130
131    // TODO: Definitely add tests to make sure this impl is right.
132    fn rotate(&mut self, value: SpirvValue, shift: SpirvValue, is_left: bool) -> SpirvValue {
133        let width = match self.lookup_type(shift.ty) {
134            SpirvType::Integer(width, _) => width,
135            other => self.fatal(format!(
136                "cannot rotate non-integer type: {}",
137                other.debug(shift.ty, self)
138            )),
139        };
140        let int_size = self.constant_int(shift.ty, width.into());
141        let mask = self.constant_int(shift.ty, (width - 1).into());
142        let zero = self.constant_int(shift.ty, 0);
143        let bool = SpirvType::Bool.def(self.span(), self);
144        // https://stackoverflow.com/a/10134877
145        let mask_shift = self.and(shift, mask);
146        let sub = self.sub(int_size, mask_shift);
147        let (lhs, rhs) = if is_left {
148            (self.shl(value, mask_shift), self.lshr(value, sub))
149        } else {
150            (self.lshr(value, mask_shift), self.shl(value, sub))
151        };
152        let or = self.or(lhs, rhs);
153        // "The result is undefined if Shift is greater than or equal to the bit width of the components of Base."
154        // So we need to check for zero shift, and don't use the shift result if it is.
155        let mask_is_zero = self
156            .emit()
157            .i_equal(bool, None, mask_shift.def(self), zero.def(self))
158            .unwrap()
159            .with_type(bool);
160        self.select(mask_is_zero, value, or)
161    }
162}
163
164// Important: This lets us use CodegenCx methods on Builder
165impl<'a, 'tcx> Deref for Builder<'a, 'tcx> {
166    type Target = CodegenCx<'tcx>;
167
168    fn deref(&self) -> &Self::Target {
169        self.cx
170    }
171}
172
173impl<'a, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'tcx> {
174    fn add_coverage(&mut self, _instance: Instance<'tcx>, _kind: &CoverageKind) {}
175}
176
177impl<'a, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'tcx> {
178    fn dbg_var_addr(
179        &mut self,
180        _dbg_var: Self::DIVariable,
181        _scope_metadata: Self::DILocation,
182        _variable_alloca: Self::Value,
183        _direct_offset: Size,
184        // NB: each offset implies a deref (i.e. they're steps in a pointer chain).
185        _indirect_offsets: &[Size],
186        _fragment: Option<Range<Size>>,
187    ) {
188        todo!()
189    }
190
191    fn set_dbg_loc(&mut self, _: Self::DILocation) {
192        todo!()
193    }
194
195    fn clear_dbg_loc(&mut self) {
196        todo!()
197    }
198
199    fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
200        todo!()
201    }
202
203    fn set_var_name(&mut self, _value: Self::Value, _name: &str) {
204        todo!()
205    }
206}
207
208impl<'a, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'a, 'tcx> {
209    fn store_fn_arg(
210        &mut self,
211        arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
212        idx: &mut usize,
213        dst: PlaceRef<'tcx, Self::Value>,
214    ) {
215        fn next(bx: &mut Builder<'_, '_>, idx: &mut usize) -> SpirvValue {
216            let val = bx.get_param(*idx);
217            *idx += 1;
218            val
219        }
220        match arg_abi.mode {
221            PassMode::Ignore => {}
222            PassMode::Direct(_) => {
223                let arg = next(self, idx);
224                self.store_arg(arg_abi, arg, dst);
225            }
226            PassMode::Pair(..) => {
227                OperandValue::Pair(next(self, idx), next(self, idx)).store(self, dst);
228            }
229            PassMode::Cast { .. } | PassMode::Indirect { .. } => span_bug!(
230                self.span(),
231                "query hooks should've made this `PassMode` impossible: {:#?}",
232                arg_abi
233            ),
234        }
235    }
236
237    fn store_arg(
238        &mut self,
239        arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
240        val: Self::Value,
241        dst: PlaceRef<'tcx, Self::Value>,
242    ) {
243        match arg_abi.mode {
244            PassMode::Ignore => {}
245            PassMode::Direct(_) | PassMode::Pair(..) => {
246                OperandRef::from_immediate_or_packed_pair(self, val, arg_abi.layout)
247                    .val
248                    .store(self, dst);
249            }
250            PassMode::Cast { .. } | PassMode::Indirect { .. } => span_bug!(
251                self.span(),
252                "query hooks should've made this `PassMode` impossible: {:#?}",
253                arg_abi
254            ),
255        }
256    }
257}
258
259impl AbiBuilderMethods for Builder<'_, '_> {
260    fn get_param(&mut self, index: usize) -> Self::Value {
261        let builder = self.emit();
262        let param =
263            &builder.module_ref().functions[builder.selected_function().unwrap()].parameters[index];
264        param
265            .result_id
266            .unwrap()
267            .with_type(param.result_type.unwrap())
268    }
269}
270
271impl<'a, 'tcx> StaticBuilderMethods for Builder<'a, 'tcx> {
272    fn get_static(&mut self, def_id: DefId) -> Self::Value {
273        self.cx.get_static(def_id)
274    }
275}
276
277impl<'a, 'tcx> BackendTypes for Builder<'a, 'tcx> {
278    type Value = <CodegenCx<'tcx> as BackendTypes>::Value;
279    type Metadata = <CodegenCx<'tcx> as BackendTypes>::Metadata;
280    type Function = <CodegenCx<'tcx> as BackendTypes>::Function;
281
282    type BasicBlock = <CodegenCx<'tcx> as BackendTypes>::BasicBlock;
283    type Type = <CodegenCx<'tcx> as BackendTypes>::Type;
284    type Funclet = <CodegenCx<'tcx> as BackendTypes>::Funclet;
285
286    type DIScope = <CodegenCx<'tcx> as BackendTypes>::DIScope;
287    type DIVariable = <CodegenCx<'tcx> as BackendTypes>::DIVariable;
288    type DILocation = <CodegenCx<'tcx> as BackendTypes>::DILocation;
289}
290
291impl<'a, 'tcx> HasTypingEnv<'tcx> for Builder<'a, 'tcx> {
292    fn typing_env(&self) -> TypingEnv<'tcx> {
293        self.cx.typing_env()
294    }
295}
296
297impl<'a, 'tcx> HasTargetSpec for Builder<'a, 'tcx> {
298    fn target_spec(&self) -> &Target {
299        self.cx.target_spec()
300    }
301}
302
303impl<'a, 'tcx> HasTyCtxt<'tcx> for Builder<'a, 'tcx> {
304    fn tcx(&self) -> TyCtxt<'tcx> {
305        self.cx.tcx
306    }
307}
308
309impl<'a, 'tcx> HasDataLayout for Builder<'a, 'tcx> {
310    fn data_layout(&self) -> &TargetDataLayout {
311        self.cx.data_layout()
312    }
313}
314
315impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, 'tcx> {
316    type LayoutOfResult = TyAndLayout<'tcx>;
317
318    #[inline]
319    fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
320        self.cx.handle_layout_err(err, span, ty)
321    }
322}
323
324impl<'tcx> FnAbiOfHelpers<'tcx> for Builder<'_, 'tcx> {
325    type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>;
326
327    #[inline]
328    fn handle_fn_abi_err(
329        &self,
330        err: FnAbiError<'tcx>,
331        span: Span,
332        fn_abi_request: FnAbiRequest<'tcx>,
333    ) -> ! {
334        self.cx.handle_fn_abi_err(err, span, fn_abi_request)
335    }
336}
337
338impl<'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'tcx> {}