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