rustc_codegen_spirv/builder/
mod.rs

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