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