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::diagnostics::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 pointee_layout.is_ssa_standalone()
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::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 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))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 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))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 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))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 let val = bx.from_immediate(val);
437 let success = bx.from_immediate(success);
438
439 let mut builder = OperandRefBuilder::new(result_layout);
440 builder.insert_imm(FieldIdx::from_u32(0), val);
441 builder.insert_imm(FieldIdx::from_u32(1), success);
442 builder.build(bx.cx()).val
443 }
444 sym::atomic_max | sym::atomic_min => {
445 let atom_op = if name == sym::atomic_max {
446 AtomicRmwBinOp::AtomicMax
447 } else {
448 AtomicRmwBinOp::AtomicMin
449 };
450
451 let ty = fn_args.type_at(0);
452 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Int(_) => true,
_ => false,
}matches!(ty.kind(), ty::Int(_)) {
453 let ordering = fn_args.const_at(1).to_value();
454 let ptr = args[0].immediate();
455 let val = args[1].immediate();
456 OperandValue::Immediate(bx.atomic_rmw(
457 atom_op,
458 ptr,
459 val,
460 parse_atomic_ordering(ordering),
461 false,
462 ))
463 } else {
464 let err = invalid_monomorphization_int_type(ty);
465 return IntrinsicResult::Err(err);
466 }
467 }
468 sym::atomic_umax | sym::atomic_umin => {
469 let atom_op = if name == sym::atomic_umax {
470 AtomicRmwBinOp::AtomicUMax
471 } else {
472 AtomicRmwBinOp::AtomicUMin
473 };
474
475 let ty = fn_args.type_at(0);
476 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Uint(_) => true,
_ => false,
}matches!(ty.kind(), ty::Uint(_)) {
477 let ordering = fn_args.const_at(1).to_value();
478 let ptr = args[0].immediate();
479 let val = args[1].immediate();
480 OperandValue::Immediate(bx.atomic_rmw(
481 atom_op,
482 ptr,
483 val,
484 parse_atomic_ordering(ordering),
485 false,
486 ))
487 } else {
488 let err = invalid_monomorphization_int_type(ty);
489 return IntrinsicResult::Err(err);
490 }
491 }
492 sym::atomic_xchg => {
493 let ty = fn_args.type_at(0);
494 let ordering = fn_args.const_at(1).to_value();
495 if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
496 let ptr = args[0].immediate();
497 let val = args[1].immediate();
498 let atomic_op = AtomicRmwBinOp::AtomicXchg;
499 OperandValue::Immediate(bx.atomic_rmw(
500 atomic_op,
501 ptr,
502 val,
503 parse_atomic_ordering(ordering),
504 ty.is_raw_ptr(),
505 ))
506 } else {
507 let err = invalid_monomorphization_int_or_ptr_type(ty);
508 return IntrinsicResult::Err(err);
509 }
510 }
511 sym::atomic_xadd
512 | sym::atomic_xsub
513 | sym::atomic_and
514 | sym::atomic_nand
515 | sym::atomic_or
516 | sym::atomic_xor => {
517 let atom_op = match name {
518 sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
519 sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
520 sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
521 sym::atomic_nand => AtomicRmwBinOp::AtomicNand,
522 sym::atomic_or => AtomicRmwBinOp::AtomicOr,
523 sym::atomic_xor => AtomicRmwBinOp::AtomicXor,
524 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
525 };
526
527 let ty_mem = fn_args.type_at(0);
529 let ty_op = fn_args.type_at(1);
531
532 let ordering = fn_args.const_at(2).to_value();
533 if (int_type_width_signed(ty_mem, bx.tcx()).is_some() && ty_op == ty_mem)
536 || (ty_mem.is_raw_ptr() && ty_op == bx.tcx().types.usize)
537 {
538 let ptr = args[0].immediate(); let val = args[1].immediate(); OperandValue::Immediate(bx.atomic_rmw(
541 atom_op,
542 ptr,
543 val,
544 parse_atomic_ordering(ordering),
545 ty_mem.is_raw_ptr(),
546 ))
547 } else {
548 let err = invalid_monomorphization_int_or_ptr_type(ty_mem);
549 return IntrinsicResult::Err(err);
550 }
551 }
552 sym::atomic_fence => {
553 let ordering = fn_args.const_at(0).to_value();
554 bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread);
555 OperandValue::ZeroSized
556 }
557
558 sym::atomic_singlethreadfence => {
559 let ordering = fn_args.const_at(0).to_value();
560 bx.atomic_fence(
561 parse_atomic_ordering(ordering),
562 SynchronizationScope::SingleThread,
563 );
564 OperandValue::ZeroSized
565 }
566
567 sym::nontemporal_store => {
568 let dst = args[0].deref(bx.cx());
569 args[1].val.nontemporal_store(bx, dst);
570 OperandValue::ZeroSized
571 }
572
573 sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
574 let ty = fn_args.type_at(0);
575 let pointee_size = bx.layout_of(ty).size;
576
577 let a = args[0].immediate();
578 let b = args[1].immediate();
579 let a = bx.ptrtoint(a, bx.type_isize());
580 let b = bx.ptrtoint(b, bx.type_isize());
581 let pointee_size = bx.const_usize(pointee_size.bytes());
582 OperandValue::Immediate(if name == sym::ptr_offset_from {
583 let d = bx.sub(a, b);
587 bx.exactsdiv(d, pointee_size)
589 } else {
590 let d = bx.unchecked_usub(a, b);
593 bx.exactudiv(d, pointee_size)
594 })
595 }
596
597 sym::cold_path => {
598 OperandValue::ZeroSized
600 }
601
602 _ => {
603 let result =
605 bx.codegen_intrinsic_call(instance, args, result_layout, result_place, span);
606 if let IntrinsicResult::Operand(op) = result {
607 op
608 } else {
609 return result;
610 }
611 }
612 };
613
614 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!(
615 op_val.is_expected_variant_for_type(result_layout),
616 "[{name:?}] Value {op_val:?} is wrong for type {result_layout:?}",
617 );
618
619 IntrinsicResult::Operand(op_val)
620 }
621}
622
623fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> {
628 match ty.kind() {
629 ty::Int(t) => {
630 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), true))
631 }
632 ty::Uint(t) => {
633 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), false))
634 }
635 _ => None,
636 }
637}
638
639fn float_type_width(ty: Ty<'_>) -> Option<u64> {
642 match ty.kind() {
643 ty::Float(t) => Some(t.bit_width()),
644 _ => None,
645 }
646}