1use rustc_abi::{Align, WrappingRange};
2use rustc_middle::mir::SourceInfo;
3use rustc_middle::ty::{self, Ty, TyCtxt};
4use rustc_middle::{bug, span_bug};
5use rustc_session::config::OptLevel;
6use rustc_span::sym;
7use rustc_target::spec::Arch;
8
9use super::FunctionCx;
10use super::operand::OperandRef;
11use super::place::PlaceRef;
12use crate::common::{AtomicRmwBinOp, SynchronizationScope};
13use crate::errors::InvalidMonomorphization;
14use crate::traits::*;
15use crate::{MemFlags, meth, size_of_val};
16
17fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
18 bx: &mut Bx,
19 allow_overlap: bool,
20 volatile: bool,
21 ty: Ty<'tcx>,
22 dst: Bx::Value,
23 src: Bx::Value,
24 count: Bx::Value,
25) {
26 let layout = bx.layout_of(ty);
27 let size = layout.size;
28 let align = layout.align.abi;
29 let size = bx.mul(bx.const_usize(size.bytes()), count);
30 let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() };
31 if allow_overlap {
32 bx.memmove(dst, align, src, align, size, flags);
33 } else {
34 bx.memcpy(dst, align, src, align, size, flags, None);
35 }
36}
37
38fn memset_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
39 bx: &mut Bx,
40 volatile: bool,
41 ty: Ty<'tcx>,
42 dst: Bx::Value,
43 val: Bx::Value,
44 count: Bx::Value,
45) {
46 let layout = bx.layout_of(ty);
47 let size = layout.size;
48 let align = layout.align.abi;
49 let size = bx.mul(bx.const_usize(size.bytes()), count);
50 let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() };
51 bx.memset(dst, val, size, align, flags);
52}
53
54impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
55 pub fn codegen_intrinsic_call(
57 &mut self,
58 bx: &mut Bx,
59 instance: ty::Instance<'tcx>,
60 args: &[OperandRef<'tcx, Bx::Value>],
61 result: PlaceRef<'tcx, Bx::Value>,
62 source_info: SourceInfo,
63 ) -> Result<(), ty::Instance<'tcx>> {
64 let span = source_info.span;
65
66 let name = bx.tcx().item_name(instance.def_id());
67 let fn_args = instance.args;
68
69 if let sym::typed_swap_nonoverlapping = name {
73 let pointee_ty = fn_args.type_at(0);
74 let pointee_layout = bx.layout_of(pointee_ty);
75 if !bx.is_backend_ref(pointee_layout)
76 || bx.sess().opts.optimize == OptLevel::No
79 || bx.sess().target.arch == Arch::SpirV
84 {
85 let align = pointee_layout.align.abi;
86 let x_place = args[0].val.deref(align);
87 let y_place = args[1].val.deref(align);
88 bx.typed_place_swap(x_place, y_place, pointee_layout);
89 return Ok(());
90 }
91 }
92
93 let invalid_monomorphization_int_type = |ty| {
94 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
95 };
96 let invalid_monomorphization_int_or_ptr_type = |ty| {
97 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerOrPtrType {
98 span,
99 name,
100 ty,
101 });
102 };
103
104 let parse_atomic_ordering = |ord: ty::Value<'tcx>| {
105 let discr = ord.to_branch()[0].to_leaf();
106 discr.to_atomic_ordering()
107 };
108
109 if args.is_empty() {
110 match name {
111 sym::abort
112 | sym::unreachable
113 | sym::cold_path
114 | sym::gpu_launch_sized_workgroup_mem
115 | sym::breakpoint
116 | sym::amdgpu_dispatch_ptr
117 | sym::assert_zero_valid
118 | sym::assert_mem_uninitialized_valid
119 | sym::assert_inhabited
120 | sym::ub_checks
121 | sym::contract_checks
122 | sym::atomic_fence
123 | sym::atomic_singlethreadfence
124 | sym::caller_location
125 | sym::return_address => {}
126 _ => {
127 ::rustc_middle::util::bug::span_bug_fmt(span,
format_args!("Nullary intrinsic {0} must be called in a const block. If you are seeing this message from code outside the standard library, the unstable implementation details of the relevant intrinsic may have changed. Consider using stable APIs instead. If you are adding a new nullary intrinsic that is inherently a runtime intrinsic, update this check.",
name));span_bug!(
128 span,
129 "Nullary intrinsic {name} must be called in a const block. \
130 If you are seeing this message from code outside the standard library, the \
131 unstable implementation details of the relevant intrinsic may have changed. \
132 Consider using stable APIs instead. \
133 If you are adding a new nullary intrinsic that is inherently a runtime \
134 intrinsic, update this check."
135 );
136 }
137 }
138 }
139
140 let llval = match name {
141 sym::abort => {
142 bx.abort();
143 return Ok(());
144 }
145
146 sym::caller_location => {
147 let location = self.get_caller_location(bx, source_info);
148 location.val.store(bx, result);
149 return Ok(());
150 }
151
152 sym::va_start => bx.va_start(args[0].immediate()),
154
155 sym::size_of_val => {
156 let tp_ty = fn_args.type_at(0);
157 let (_, meta) = args[0].val.pointer_parts();
158 let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta);
159 llsize
160 }
161 sym::align_of_val => {
162 let tp_ty = fn_args.type_at(0);
163 let (_, meta) = args[0].val.pointer_parts();
164 let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta);
165 llalign
166 }
167 sym::vtable_size | sym::vtable_align => {
168 let vtable = args[0].immediate();
169 let idx = match name {
170 sym::vtable_size => ty::COMMON_VTABLE_ENTRIES_SIZE,
171 sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN,
172 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
173 };
174 let value = meth::VirtualIndex::from_index(idx).get_usize(
175 bx,
176 vtable,
177 instance.ty(bx.tcx(), bx.typing_env()),
178 );
179 match name {
180 sym::vtable_size => {
182 let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
183 bx.range_metadata(value, WrappingRange { start: 0, end: size_bound });
184 }
185 sym::vtable_align => {
188 let align_bound = Align::max_for_target(bx.data_layout()).bytes().into();
189 bx.range_metadata(value, WrappingRange { start: 1, end: align_bound })
190 }
191 _ => {}
192 }
193 value
194 }
195 sym::arith_offset => {
196 let ty = fn_args.type_at(0);
197 let layout = bx.layout_of(ty);
198 let ptr = args[0].immediate();
199 let offset = args[1].immediate();
200 bx.gep(bx.backend_type(layout), ptr, &[offset])
201 }
202 sym::copy => {
203 copy_intrinsic(
204 bx,
205 true,
206 false,
207 fn_args.type_at(0),
208 args[1].immediate(),
209 args[0].immediate(),
210 args[2].immediate(),
211 );
212 return Ok(());
213 }
214 sym::write_bytes => {
215 memset_intrinsic(
216 bx,
217 false,
218 fn_args.type_at(0),
219 args[0].immediate(),
220 args[1].immediate(),
221 args[2].immediate(),
222 );
223 return Ok(());
224 }
225
226 sym::volatile_copy_nonoverlapping_memory => {
227 copy_intrinsic(
228 bx,
229 false,
230 true,
231 fn_args.type_at(0),
232 args[0].immediate(),
233 args[1].immediate(),
234 args[2].immediate(),
235 );
236 return Ok(());
237 }
238 sym::volatile_copy_memory => {
239 copy_intrinsic(
240 bx,
241 true,
242 true,
243 fn_args.type_at(0),
244 args[0].immediate(),
245 args[1].immediate(),
246 args[2].immediate(),
247 );
248 return Ok(());
249 }
250 sym::volatile_set_memory => {
251 memset_intrinsic(
252 bx,
253 true,
254 fn_args.type_at(0),
255 args[0].immediate(),
256 args[1].immediate(),
257 args[2].immediate(),
258 );
259 return Ok(());
260 }
261 sym::volatile_store => {
262 let dst = args[0].deref(bx.cx());
263 args[1].val.volatile_store(bx, dst);
264 return Ok(());
265 }
266 sym::unaligned_volatile_store => {
267 let dst = args[0].deref(bx.cx());
268 args[1].val.unaligned_volatile_store(bx, dst);
269 return Ok(());
270 }
271 sym::disjoint_bitor => {
272 let a = args[0].immediate();
273 let b = args[1].immediate();
274 bx.or_disjoint(a, b)
275 }
276 sym::exact_div => {
277 let ty = args[0].layout.ty;
278 match int_type_width_signed(ty, bx.tcx()) {
279 Some((_width, signed)) => {
280 if signed {
281 bx.exactsdiv(args[0].immediate(), args[1].immediate())
282 } else {
283 bx.exactudiv(args[0].immediate(), args[1].immediate())
284 }
285 }
286 None => {
287 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
288 span,
289 name,
290 ty,
291 });
292 return Ok(());
293 }
294 }
295 }
296 sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
297 match float_type_width(args[0].layout.ty) {
298 Some(_width) => match name {
299 sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()),
300 sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()),
301 sym::fmul_fast => bx.fmul_fast(args[0].immediate(), args[1].immediate()),
302 sym::fdiv_fast => bx.fdiv_fast(args[0].immediate(), args[1].immediate()),
303 sym::frem_fast => bx.frem_fast(args[0].immediate(), args[1].immediate()),
304 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
305 },
306 None => {
307 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
308 span,
309 name,
310 ty: args[0].layout.ty,
311 });
312 return Ok(());
313 }
314 }
315 }
316 sym::fadd_algebraic
317 | sym::fsub_algebraic
318 | sym::fmul_algebraic
319 | sym::fdiv_algebraic
320 | sym::frem_algebraic => match float_type_width(args[0].layout.ty) {
321 Some(_width) => match name {
322 sym::fadd_algebraic => {
323 bx.fadd_algebraic(args[0].immediate(), args[1].immediate())
324 }
325 sym::fsub_algebraic => {
326 bx.fsub_algebraic(args[0].immediate(), args[1].immediate())
327 }
328 sym::fmul_algebraic => {
329 bx.fmul_algebraic(args[0].immediate(), args[1].immediate())
330 }
331 sym::fdiv_algebraic => {
332 bx.fdiv_algebraic(args[0].immediate(), args[1].immediate())
333 }
334 sym::frem_algebraic => {
335 bx.frem_algebraic(args[0].immediate(), args[1].immediate())
336 }
337 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
338 },
339 None => {
340 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
341 span,
342 name,
343 ty: args[0].layout.ty,
344 });
345 return Ok(());
346 }
347 },
348
349 sym::float_to_int_unchecked => {
350 if float_type_width(args[0].layout.ty).is_none() {
351 bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
352 span,
353 ty: args[0].layout.ty,
354 });
355 return Ok(());
356 }
357 let Some((_width, signed)) = int_type_width_signed(result.layout.ty, bx.tcx())
358 else {
359 bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
360 span,
361 ty: result.layout.ty,
362 });
363 return Ok(());
364 };
365 if signed {
366 bx.fptosi(args[0].immediate(), bx.backend_type(result.layout))
367 } else {
368 bx.fptoui(args[0].immediate(), bx.backend_type(result.layout))
369 }
370 }
371
372 sym::atomic_load => {
373 let ty = fn_args.type_at(0);
374 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
375 invalid_monomorphization_int_or_ptr_type(ty);
376 return Ok(());
377 }
378 let ordering = fn_args.const_at(1).to_value();
379 let layout = bx.layout_of(ty);
380 let source = args[0].immediate();
381 bx.atomic_load(
382 bx.backend_type(layout),
383 source,
384 parse_atomic_ordering(ordering),
385 layout.size,
386 )
387 }
388 sym::atomic_store => {
389 let ty = fn_args.type_at(0);
390 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
391 invalid_monomorphization_int_or_ptr_type(ty);
392 return Ok(());
393 }
394 let ordering = fn_args.const_at(1).to_value();
395 let size = bx.layout_of(ty).size;
396 let val = args[1].immediate();
397 let ptr = args[0].immediate();
398 bx.atomic_store(val, ptr, parse_atomic_ordering(ordering), size);
399 return Ok(());
400 }
401 sym::atomic_cxchg | sym::atomic_cxchgweak => {
403 let ty = fn_args.type_at(0);
404 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
405 invalid_monomorphization_int_or_ptr_type(ty);
406 return Ok(());
407 }
408 let succ_ordering = fn_args.const_at(1).to_value();
409 let fail_ordering = fn_args.const_at(2).to_value();
410 let weak = name == sym::atomic_cxchgweak;
411 let dst = args[0].immediate();
412 let cmp = args[1].immediate();
413 let src = args[2].immediate();
414 let (val, success) = bx.atomic_cmpxchg(
415 dst,
416 cmp,
417 src,
418 parse_atomic_ordering(succ_ordering),
419 parse_atomic_ordering(fail_ordering),
420 weak,
421 );
422 let val = bx.from_immediate(val);
423 let success = bx.from_immediate(success);
424
425 let dest = result.project_field(bx, 0);
426 bx.store_to_place(val, dest.val);
427 let dest = result.project_field(bx, 1);
428 bx.store_to_place(success, dest.val);
429
430 return Ok(());
431 }
432 sym::atomic_max | sym::atomic_min => {
433 let atom_op = if name == sym::atomic_max {
434 AtomicRmwBinOp::AtomicMax
435 } else {
436 AtomicRmwBinOp::AtomicMin
437 };
438
439 let ty = fn_args.type_at(0);
440 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Int(_) => true,
_ => false,
}matches!(ty.kind(), ty::Int(_)) {
441 let ordering = fn_args.const_at(1).to_value();
442 let ptr = args[0].immediate();
443 let val = args[1].immediate();
444 bx.atomic_rmw(
445 atom_op,
446 ptr,
447 val,
448 parse_atomic_ordering(ordering),
449 false,
450 )
451 } else {
452 invalid_monomorphization_int_type(ty);
453 return Ok(());
454 }
455 }
456 sym::atomic_umax | sym::atomic_umin => {
457 let atom_op = if name == sym::atomic_umax {
458 AtomicRmwBinOp::AtomicUMax
459 } else {
460 AtomicRmwBinOp::AtomicUMin
461 };
462
463 let ty = fn_args.type_at(0);
464 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Uint(_) => true,
_ => false,
}matches!(ty.kind(), ty::Uint(_)) {
465 let ordering = fn_args.const_at(1).to_value();
466 let ptr = args[0].immediate();
467 let val = args[1].immediate();
468 bx.atomic_rmw(
469 atom_op,
470 ptr,
471 val,
472 parse_atomic_ordering(ordering),
473 false,
474 )
475 } else {
476 invalid_monomorphization_int_type(ty);
477 return Ok(());
478 }
479 }
480 sym::atomic_xchg => {
481 let ty = fn_args.type_at(0);
482 let ordering = fn_args.const_at(1).to_value();
483 if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
484 let ptr = args[0].immediate();
485 let val = args[1].immediate();
486 let atomic_op = AtomicRmwBinOp::AtomicXchg;
487 bx.atomic_rmw(
488 atomic_op,
489 ptr,
490 val,
491 parse_atomic_ordering(ordering),
492 ty.is_raw_ptr(),
493 )
494 } else {
495 invalid_monomorphization_int_or_ptr_type(ty);
496 return Ok(());
497 }
498 }
499 sym::atomic_xadd
500 | sym::atomic_xsub
501 | sym::atomic_and
502 | sym::atomic_nand
503 | sym::atomic_or
504 | sym::atomic_xor => {
505 let atom_op = match name {
506 sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
507 sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
508 sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
509 sym::atomic_nand => AtomicRmwBinOp::AtomicNand,
510 sym::atomic_or => AtomicRmwBinOp::AtomicOr,
511 sym::atomic_xor => AtomicRmwBinOp::AtomicXor,
512 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
513 };
514
515 let ty_mem = fn_args.type_at(0);
517 let ty_op = fn_args.type_at(1);
519
520 let ordering = fn_args.const_at(2).to_value();
521 if (int_type_width_signed(ty_mem, bx.tcx()).is_some() && ty_op == ty_mem)
524 || (ty_mem.is_raw_ptr() && ty_op == bx.tcx().types.usize)
525 {
526 let ptr = args[0].immediate(); let val = args[1].immediate(); bx.atomic_rmw(
529 atom_op,
530 ptr,
531 val,
532 parse_atomic_ordering(ordering),
533 ty_mem.is_raw_ptr(),
534 )
535 } else {
536 invalid_monomorphization_int_or_ptr_type(ty_mem);
537 return Ok(());
538 }
539 }
540 sym::atomic_fence => {
541 let ordering = fn_args.const_at(0).to_value();
542 bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread);
543 return Ok(());
544 }
545
546 sym::atomic_singlethreadfence => {
547 let ordering = fn_args.const_at(0).to_value();
548 bx.atomic_fence(
549 parse_atomic_ordering(ordering),
550 SynchronizationScope::SingleThread,
551 );
552 return Ok(());
553 }
554
555 sym::nontemporal_store => {
556 let dst = args[0].deref(bx.cx());
557 args[1].val.nontemporal_store(bx, dst);
558 return Ok(());
559 }
560
561 sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
562 let ty = fn_args.type_at(0);
563 let pointee_size = bx.layout_of(ty).size;
564
565 let a = args[0].immediate();
566 let b = args[1].immediate();
567 let a = bx.ptrtoint(a, bx.type_isize());
568 let b = bx.ptrtoint(b, bx.type_isize());
569 let pointee_size = bx.const_usize(pointee_size.bytes());
570 if name == sym::ptr_offset_from {
571 let d = bx.sub(a, b);
575 bx.exactsdiv(d, pointee_size)
577 } else {
578 let d = bx.unchecked_usub(a, b);
581 bx.exactudiv(d, pointee_size)
582 }
583 }
584
585 sym::cold_path => {
586 return Ok(());
588 }
589
590 _ => {
591 return bx.codegen_intrinsic_call(instance, args, result, span);
593 }
594 };
595
596 if result.layout.ty.is_bool() {
597 let val = bx.from_immediate(llval);
598 bx.store_to_place(val, result.val);
599 } else if !result.layout.ty.is_unit() {
600 bx.store_to_place(llval, result.val);
601 }
602 Ok(())
603 }
604}
605
606fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> {
611 match ty.kind() {
612 ty::Int(t) => {
613 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), true))
614 }
615 ty::Uint(t) => {
616 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), false))
617 }
618 _ => None,
619 }
620}
621
622fn float_type_width(ty: Ty<'_>) -> Option<u64> {
625 match ty.kind() {
626 ty::Float(t) => Some(t.bit_width()),
627 _ => None,
628 }
629}