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 | sym::unaligned_volatile_store => {
277 let dst = args[0].deref(bx.cx());
278 let dst = if name == sym::volatile_store { dst } else { dst.unaligned() };
279 args[1].val.volatile_store(bx, dst);
280 OperandValue::ZeroSized
281 }
282 sym::disjoint_bitor => {
283 let a = args[0].immediate();
284 let b = args[1].immediate();
285 OperandValue::Immediate(bx.or_disjoint(a, b))
286 }
287 sym::exact_div => {
288 let ty = args[0].layout.ty;
289 match int_type_width_signed(ty, bx.tcx()) {
290 Some((_width, signed)) => OperandValue::Immediate(if signed {
291 bx.exactsdiv(args[0].immediate(), args[1].immediate())
292 } else {
293 bx.exactudiv(args[0].immediate(), args[1].immediate())
294 }),
295 None => {
296 let err = bx
297 .tcx()
298 .dcx()
299 .emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
300 return IntrinsicResult::Err(err);
301 }
302 }
303 }
304 sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
305 match float_type_width(args[0].layout.ty) {
306 Some(_width) => OperandValue::Immediate(match name {
307 sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()),
308 sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()),
309 sym::fmul_fast => bx.fmul_fast(args[0].immediate(), args[1].immediate()),
310 sym::fdiv_fast => bx.fdiv_fast(args[0].immediate(), args[1].immediate()),
311 sym::frem_fast => bx.frem_fast(args[0].immediate(), args[1].immediate()),
312 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
313 }),
314 None => {
315 let err =
316 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
317 span,
318 name,
319 ty: args[0].layout.ty,
320 });
321 return IntrinsicResult::Err(err);
322 }
323 }
324 }
325 sym::fadd_algebraic
326 | sym::fsub_algebraic
327 | sym::fmul_algebraic
328 | sym::fdiv_algebraic
329 | sym::frem_algebraic => match float_type_width(args[0].layout.ty) {
330 Some(_width) => OperandValue::Immediate(match name {
331 sym::fadd_algebraic => {
332 bx.fadd_algebraic(args[0].immediate(), args[1].immediate())
333 }
334 sym::fsub_algebraic => {
335 bx.fsub_algebraic(args[0].immediate(), args[1].immediate())
336 }
337 sym::fmul_algebraic => {
338 bx.fmul_algebraic(args[0].immediate(), args[1].immediate())
339 }
340 sym::fdiv_algebraic => {
341 bx.fdiv_algebraic(args[0].immediate(), args[1].immediate())
342 }
343 sym::frem_algebraic => {
344 bx.frem_algebraic(args[0].immediate(), args[1].immediate())
345 }
346 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
347 }),
348 None => {
349 let err = bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
350 span,
351 name,
352 ty: args[0].layout.ty,
353 });
354 return IntrinsicResult::Err(err);
355 }
356 },
357
358 sym::float_to_int_unchecked => {
359 if float_type_width(args[0].layout.ty).is_none() {
360 let err =
361 bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
362 span,
363 ty: args[0].layout.ty,
364 });
365 return IntrinsicResult::Err(err);
366 }
367 let Some((_width, signed)) = int_type_width_signed(result_layout.ty, bx.tcx())
368 else {
369 let err =
370 bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
371 span,
372 ty: result_layout.ty,
373 });
374 return IntrinsicResult::Err(err);
375 };
376 OperandValue::Immediate(if signed {
377 bx.fptosi(args[0].immediate(), bx.backend_type(result_layout))
378 } else {
379 bx.fptoui(args[0].immediate(), bx.backend_type(result_layout))
380 })
381 }
382
383 sym::atomic_load => {
384 let ty = fn_args.type_at(0);
385 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
386 let err = invalid_monomorphization_int_or_ptr_type(ty);
387 return IntrinsicResult::Err(err);
388 }
389 let ordering = fn_args.const_at(1).to_value();
390 let layout = bx.layout_of(ty);
391 let source = args[0].immediate();
392 OperandValue::Immediate(bx.atomic_load(
393 bx.backend_type(layout),
394 source,
395 parse_atomic_ordering(ordering),
396 layout.size,
397 ))
398 }
399 sym::atomic_store => {
400 let ty = fn_args.type_at(0);
401 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
402 let err = invalid_monomorphization_int_or_ptr_type(ty);
403 return IntrinsicResult::Err(err);
404 }
405 let ordering = fn_args.const_at(1).to_value();
406 let size = bx.layout_of(ty).size;
407 let val = args[1].immediate();
408 let ptr = args[0].immediate();
409 bx.atomic_store(val, ptr, parse_atomic_ordering(ordering), size);
410 OperandValue::ZeroSized
411 }
412 sym::atomic_cxchg | sym::atomic_cxchgweak => {
414 let ty = fn_args.type_at(0);
415 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
416 let err = invalid_monomorphization_int_or_ptr_type(ty);
417 return IntrinsicResult::Err(err);
418 }
419 let succ_ordering = fn_args.const_at(1).to_value();
420 let fail_ordering = fn_args.const_at(2).to_value();
421 let weak = name == sym::atomic_cxchgweak;
422 let dst = args[0].immediate();
423 let cmp = args[1].immediate();
424 let src = args[2].immediate();
425 let (val, success) = bx.atomic_cmpxchg(
426 dst,
427 cmp,
428 src,
429 parse_atomic_ordering(succ_ordering),
430 parse_atomic_ordering(fail_ordering),
431 weak,
432 );
433 let val = bx.from_immediate(val);
434 let success = bx.from_immediate(success);
435
436 let mut builder = OperandRefBuilder::new(result_layout);
437 builder.insert_imm(FieldIdx::from_u32(0), val);
438 builder.insert_imm(FieldIdx::from_u32(1), success);
439 builder.build(bx.cx()).val
440 }
441 sym::atomic_max | sym::atomic_min => {
442 let atom_op = if name == sym::atomic_max {
443 AtomicRmwBinOp::AtomicMax
444 } else {
445 AtomicRmwBinOp::AtomicMin
446 };
447
448 let ty = fn_args.type_at(0);
449 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Int(_) => true,
_ => false,
}matches!(ty.kind(), ty::Int(_)) {
450 let ordering = fn_args.const_at(1).to_value();
451 let ptr = args[0].immediate();
452 let val = args[1].immediate();
453 OperandValue::Immediate(bx.atomic_rmw(
454 atom_op,
455 ptr,
456 val,
457 parse_atomic_ordering(ordering),
458 false,
459 ))
460 } else {
461 let err = invalid_monomorphization_int_type(ty);
462 return IntrinsicResult::Err(err);
463 }
464 }
465 sym::atomic_umax | sym::atomic_umin => {
466 let atom_op = if name == sym::atomic_umax {
467 AtomicRmwBinOp::AtomicUMax
468 } else {
469 AtomicRmwBinOp::AtomicUMin
470 };
471
472 let ty = fn_args.type_at(0);
473 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Uint(_) => true,
_ => false,
}matches!(ty.kind(), ty::Uint(_)) {
474 let ordering = fn_args.const_at(1).to_value();
475 let ptr = args[0].immediate();
476 let val = args[1].immediate();
477 OperandValue::Immediate(bx.atomic_rmw(
478 atom_op,
479 ptr,
480 val,
481 parse_atomic_ordering(ordering),
482 false,
483 ))
484 } else {
485 let err = invalid_monomorphization_int_type(ty);
486 return IntrinsicResult::Err(err);
487 }
488 }
489 sym::atomic_xchg => {
490 let ty = fn_args.type_at(0);
491 let ordering = fn_args.const_at(1).to_value();
492 if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
493 let ptr = args[0].immediate();
494 let val = args[1].immediate();
495 let atomic_op = AtomicRmwBinOp::AtomicXchg;
496 OperandValue::Immediate(bx.atomic_rmw(
497 atomic_op,
498 ptr,
499 val,
500 parse_atomic_ordering(ordering),
501 ty.is_raw_ptr(),
502 ))
503 } else {
504 let err = invalid_monomorphization_int_or_ptr_type(ty);
505 return IntrinsicResult::Err(err);
506 }
507 }
508 sym::atomic_xadd
509 | sym::atomic_xsub
510 | sym::atomic_and
511 | sym::atomic_nand
512 | sym::atomic_or
513 | sym::atomic_xor => {
514 let atom_op = match name {
515 sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
516 sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
517 sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
518 sym::atomic_nand => AtomicRmwBinOp::AtomicNand,
519 sym::atomic_or => AtomicRmwBinOp::AtomicOr,
520 sym::atomic_xor => AtomicRmwBinOp::AtomicXor,
521 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
522 };
523
524 let ty_mem = fn_args.type_at(0);
526 let ty_op = fn_args.type_at(1);
528
529 let ordering = fn_args.const_at(2).to_value();
530 if (int_type_width_signed(ty_mem, bx.tcx()).is_some() && ty_op == ty_mem)
533 || (ty_mem.is_raw_ptr() && ty_op == bx.tcx().types.usize)
534 {
535 let ptr = args[0].immediate(); let val = args[1].immediate(); OperandValue::Immediate(bx.atomic_rmw(
538 atom_op,
539 ptr,
540 val,
541 parse_atomic_ordering(ordering),
542 ty_mem.is_raw_ptr(),
543 ))
544 } else {
545 let err = invalid_monomorphization_int_or_ptr_type(ty_mem);
546 return IntrinsicResult::Err(err);
547 }
548 }
549 sym::atomic_fence => {
550 let ordering = fn_args.const_at(0).to_value();
551 bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread);
552 OperandValue::ZeroSized
553 }
554
555 sym::atomic_singlethreadfence => {
556 let ordering = fn_args.const_at(0).to_value();
557 bx.atomic_fence(
558 parse_atomic_ordering(ordering),
559 SynchronizationScope::SingleThread,
560 );
561 OperandValue::ZeroSized
562 }
563
564 sym::nontemporal_store => {
565 let dst = args[0].deref(bx.cx());
566 args[1].val.nontemporal_store(bx, dst);
567 OperandValue::ZeroSized
568 }
569
570 sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
571 let ty = fn_args.type_at(0);
572 let pointee_size = bx.layout_of(ty).size;
573
574 let a = args[0].immediate();
575 let b = args[1].immediate();
576 let a = bx.ptrtoint(a, bx.type_isize());
577 let b = bx.ptrtoint(b, bx.type_isize());
578 let pointee_size = bx.const_usize(pointee_size.bytes());
579 OperandValue::Immediate(if name == sym::ptr_offset_from {
580 let d = bx.sub(a, b);
584 bx.exactsdiv(d, pointee_size)
586 } else {
587 let d = bx.unchecked_usub(a, b);
590 bx.exactudiv(d, pointee_size)
591 })
592 }
593
594 sym::cold_path => {
595 OperandValue::ZeroSized
597 }
598
599 _ => {
600 let result =
602 bx.codegen_intrinsic_call(instance, args, result_layout, result_place, span);
603 if let IntrinsicResult::Operand(op) = result {
604 op
605 } else {
606 return result;
607 }
608 }
609 };
610
611 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!(
612 op_val.is_expected_variant_for_type(bx.cx(), result_layout),
613 "[{name:?}] Value {op_val:?} is wrong for type {result_layout:?}",
614 );
615
616 IntrinsicResult::Operand(op_val)
617 }
618}
619
620fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> {
625 match ty.kind() {
626 ty::Int(t) => {
627 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), true))
628 }
629 ty::Uint(t) => {
630 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), false))
631 }
632 _ => None,
633 }
634}
635
636fn float_type_width(ty: Ty<'_>) -> Option<u64> {
639 match ty.kind() {
640 ty::Float(t) => Some(t.bit_width()),
641 _ => None,
642 }
643}