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::offload_get_num_devices
139 | sym::return_address => {}
140 _ => {
141 ::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!(
142 span,
143 "Nullary intrinsic {name} must be called in a const block. \
144 If you are seeing this message from code outside the standard library, the \
145 unstable implementation details of the relevant intrinsic may have changed. \
146 Consider using stable APIs instead. \
147 If you are adding a new nullary intrinsic that is inherently a runtime \
148 intrinsic, update this check."
149 );
150 }
151 }
152 }
153
154 let op_val: OperandValue<_> = match name {
155 sym::abort => {
156 bx.abort();
157 OperandValue::ZeroSized
158 }
159
160 sym::caller_location => {
161 let location = self.get_caller_location(bx, source_info);
162 location.val
163 }
164
165 sym::size_of_val => {
166 let tp_ty = fn_args.type_at(0);
167 let (_, meta) = args[0].val.pointer_parts();
168 let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta, span);
169 OperandValue::Immediate(llsize)
170 }
171 sym::align_of_val => {
172 let tp_ty = fn_args.type_at(0);
173 let (_, meta) = args[0].val.pointer_parts();
174 let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta, span);
175 OperandValue::Immediate(llalign)
176 }
177 sym::vtable_size | sym::vtable_align => {
178 let vtable = args[0].immediate();
179 let idx = match name {
180 sym::vtable_size => ty::COMMON_VTABLE_ENTRIES_SIZE,
181 sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN,
182 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
183 };
184 let value = meth::VirtualIndex::from_index(idx).get_usize(
185 bx,
186 vtable,
187 instance.ty(bx.tcx(), bx.typing_env()),
188 );
189 match name {
190 sym::vtable_size => {
192 let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
193 bx.range_metadata(value, WrappingRange { start: 0, end: size_bound });
194 }
195 sym::vtable_align => {
198 let align_bound = Align::max_for_target(bx.data_layout()).bytes().into();
199 bx.range_metadata(value, WrappingRange { start: 1, end: align_bound })
200 }
201 _ => {}
202 }
203 OperandValue::Immediate(value)
204 }
205 sym::arith_offset => {
206 let ty = fn_args.type_at(0);
207 let layout = bx.layout_of(ty);
208 let ptr = args[0].immediate();
209 let offset = args[1].immediate();
210 OperandValue::Immediate(bx.gep(bx.backend_type(layout), ptr, &[offset]))
211 }
212 sym::copy => {
213 copy_intrinsic(
214 bx,
215 true,
216 false,
217 fn_args.type_at(0),
218 args[1].immediate(),
219 args[0].immediate(),
220 args[2].immediate(),
221 );
222 OperandValue::ZeroSized
223 }
224 sym::write_bytes => {
225 memset_intrinsic(
226 bx,
227 false,
228 fn_args.type_at(0),
229 args[0].immediate(),
230 args[1].immediate(),
231 args[2].immediate(),
232 );
233 OperandValue::ZeroSized
234 }
235
236 sym::volatile_copy_nonoverlapping_memory => {
237 copy_intrinsic(
238 bx,
239 false,
240 true,
241 fn_args.type_at(0),
242 args[0].immediate(),
243 args[1].immediate(),
244 args[2].immediate(),
245 );
246 OperandValue::ZeroSized
247 }
248 sym::volatile_copy_memory => {
249 copy_intrinsic(
250 bx,
251 true,
252 true,
253 fn_args.type_at(0),
254 args[0].immediate(),
255 args[1].immediate(),
256 args[2].immediate(),
257 );
258 OperandValue::ZeroSized
259 }
260 sym::volatile_set_memory => {
261 memset_intrinsic(
262 bx,
263 true,
264 fn_args.type_at(0),
265 args[0].immediate(),
266 args[1].immediate(),
267 args[2].immediate(),
268 );
269 OperandValue::ZeroSized
270 }
271 sym::volatile_store | sym::unaligned_volatile_store => {
272 let dst = args[0].deref(bx.cx());
273 let dst = if name == sym::volatile_store { dst } else { dst.unaligned() };
274 args[1].val.volatile_store(bx, dst);
275 OperandValue::ZeroSized
276 }
277 sym::disjoint_bitor => {
278 let a = args[0].immediate();
279 let b = args[1].immediate();
280 OperandValue::Immediate(bx.or_disjoint(a, b))
281 }
282 sym::exact_div => {
283 let ty = args[0].layout.ty;
284 match int_type_width_signed(ty, bx.tcx()) {
285 Some((_width, signed)) => OperandValue::Immediate(if signed {
286 bx.exactsdiv(args[0].immediate(), args[1].immediate())
287 } else {
288 bx.exactudiv(args[0].immediate(), args[1].immediate())
289 }),
290 None => {
291 let err = bx
292 .tcx()
293 .dcx()
294 .emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
295 return IntrinsicResult::Err(err);
296 }
297 }
298 }
299 sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
300 match float_type_width(args[0].layout.ty) {
301 Some(_width) => OperandValue::Immediate(match name {
302 sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()),
303 sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()),
304 sym::fmul_fast => bx.fmul_fast(args[0].immediate(), args[1].immediate()),
305 sym::fdiv_fast => bx.fdiv_fast(args[0].immediate(), args[1].immediate()),
306 sym::frem_fast => bx.frem_fast(args[0].immediate(), args[1].immediate()),
307 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
308 }),
309 None => {
310 let err =
311 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
312 span,
313 name,
314 ty: args[0].layout.ty,
315 });
316 return IntrinsicResult::Err(err);
317 }
318 }
319 }
320 sym::fadd_algebraic
321 | sym::fsub_algebraic
322 | sym::fmul_algebraic
323 | sym::fdiv_algebraic
324 | sym::frem_algebraic => match float_type_width(args[0].layout.ty) {
325 Some(_width) => OperandValue::Immediate(match name {
326 sym::fadd_algebraic => {
327 bx.fadd_algebraic(args[0].immediate(), args[1].immediate())
328 }
329 sym::fsub_algebraic => {
330 bx.fsub_algebraic(args[0].immediate(), args[1].immediate())
331 }
332 sym::fmul_algebraic => {
333 bx.fmul_algebraic(args[0].immediate(), args[1].immediate())
334 }
335 sym::fdiv_algebraic => {
336 bx.fdiv_algebraic(args[0].immediate(), args[1].immediate())
337 }
338 sym::frem_algebraic => {
339 bx.frem_algebraic(args[0].immediate(), args[1].immediate())
340 }
341 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
342 }),
343 None => {
344 let err = bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
345 span,
346 name,
347 ty: args[0].layout.ty,
348 });
349 return IntrinsicResult::Err(err);
350 }
351 },
352
353 sym::float_to_int_unchecked => {
354 if float_type_width(args[0].layout.ty).is_none() {
355 let err =
356 bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
357 span,
358 ty: args[0].layout.ty,
359 });
360 return IntrinsicResult::Err(err);
361 }
362 let Some((_width, signed)) = int_type_width_signed(result_layout.ty, bx.tcx())
363 else {
364 let err =
365 bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
366 span,
367 ty: result_layout.ty,
368 });
369 return IntrinsicResult::Err(err);
370 };
371 OperandValue::Immediate(if signed {
372 bx.fptosi(args[0].immediate(), bx.backend_type(result_layout))
373 } else {
374 bx.fptoui(args[0].immediate(), bx.backend_type(result_layout))
375 })
376 }
377
378 sym::atomic_load => {
379 let ty = fn_args.type_at(0);
380 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
381 let err = invalid_monomorphization_int_or_ptr_type(ty);
382 return IntrinsicResult::Err(err);
383 }
384 let ordering = fn_args.const_at(1).to_value();
385 let volatile = fn_args.const_at(2).to_value();
386 let layout = bx.layout_of(ty);
387 let source = args[0].immediate();
388 OperandValue::Immediate(bx.atomic_load(
389 bx.backend_type(layout),
390 source,
391 parse_atomic_ordering(ordering),
392 volatile.to_leaf().try_to_bool().unwrap(),
393 layout.size,
394 ))
395 }
396 sym::atomic_store => {
397 let ty = fn_args.type_at(0);
398 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
399 let err = invalid_monomorphization_int_or_ptr_type(ty);
400 return IntrinsicResult::Err(err);
401 }
402 let ordering = fn_args.const_at(1).to_value();
403 let volatile = fn_args.const_at(2).to_value();
404 let size = bx.layout_of(ty).size;
405 let val = args[1].immediate();
406 let ptr = args[0].immediate();
407 bx.atomic_store(
408 val,
409 ptr,
410 parse_atomic_ordering(ordering),
411 volatile.to_leaf().try_to_bool().unwrap(),
412 size,
413 );
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
438 let mut builder = OperandRefBuilder::new(result_layout);
439 builder.insert_imm(FieldIdx::from_u32(0), val);
440 builder.insert_imm(FieldIdx::from_u32(1), success);
441 builder.build(bx.cx()).val
442 }
443 sym::atomic_max | sym::atomic_min => {
444 let atom_op = if name == sym::atomic_max {
445 AtomicRmwBinOp::AtomicMax
446 } else {
447 AtomicRmwBinOp::AtomicMin
448 };
449
450 let ty = fn_args.type_at(0);
451 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Int(_) => true,
_ => false,
}matches!(ty.kind(), ty::Int(_)) {
452 let ordering = fn_args.const_at(1).to_value();
453 let ptr = args[0].immediate();
454 let val = args[1].immediate();
455 OperandValue::Immediate(bx.atomic_rmw(
456 atom_op,
457 ptr,
458 val,
459 parse_atomic_ordering(ordering),
460 false,
461 ))
462 } else {
463 let err = invalid_monomorphization_int_type(ty);
464 return IntrinsicResult::Err(err);
465 }
466 }
467 sym::atomic_umax | sym::atomic_umin => {
468 let atom_op = if name == sym::atomic_umax {
469 AtomicRmwBinOp::AtomicUMax
470 } else {
471 AtomicRmwBinOp::AtomicUMin
472 };
473
474 let ty = fn_args.type_at(0);
475 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Uint(_) => true,
_ => false,
}matches!(ty.kind(), ty::Uint(_)) {
476 let ordering = fn_args.const_at(1).to_value();
477 let ptr = args[0].immediate();
478 let val = args[1].immediate();
479 OperandValue::Immediate(bx.atomic_rmw(
480 atom_op,
481 ptr,
482 val,
483 parse_atomic_ordering(ordering),
484 false,
485 ))
486 } else {
487 let err = invalid_monomorphization_int_type(ty);
488 return IntrinsicResult::Err(err);
489 }
490 }
491 sym::atomic_xchg => {
492 let ty = fn_args.type_at(0);
493 let ordering = fn_args.const_at(1).to_value();
494 if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
495 let ptr = args[0].immediate();
496 let val = args[1].immediate();
497 let atomic_op = AtomicRmwBinOp::AtomicXchg;
498 OperandValue::Immediate(bx.atomic_rmw(
499 atomic_op,
500 ptr,
501 val,
502 parse_atomic_ordering(ordering),
503 ty.is_raw_ptr(),
504 ))
505 } else {
506 let err = invalid_monomorphization_int_or_ptr_type(ty);
507 return IntrinsicResult::Err(err);
508 }
509 }
510 sym::atomic_xadd
511 | sym::atomic_xsub
512 | sym::atomic_and
513 | sym::atomic_nand
514 | sym::atomic_or
515 | sym::atomic_xor => {
516 let atom_op = match name {
517 sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
518 sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
519 sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
520 sym::atomic_nand => AtomicRmwBinOp::AtomicNand,
521 sym::atomic_or => AtomicRmwBinOp::AtomicOr,
522 sym::atomic_xor => AtomicRmwBinOp::AtomicXor,
523 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
524 };
525
526 let ty_mem = fn_args.type_at(0);
528 let ty_op = fn_args.type_at(1);
530
531 let ordering = fn_args.const_at(2).to_value();
532 if (int_type_width_signed(ty_mem, bx.tcx()).is_some() && ty_op == ty_mem)
535 || (ty_mem.is_raw_ptr() && ty_op == bx.tcx().types.usize)
536 {
537 let ptr = args[0].immediate(); let val = args[1].immediate(); OperandValue::Immediate(bx.atomic_rmw(
540 atom_op,
541 ptr,
542 val,
543 parse_atomic_ordering(ordering),
544 ty_mem.is_raw_ptr(),
545 ))
546 } else {
547 let err = invalid_monomorphization_int_or_ptr_type(ty_mem);
548 return IntrinsicResult::Err(err);
549 }
550 }
551 sym::atomic_fence => {
552 let ordering = fn_args.const_at(0).to_value();
553 bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread);
554 OperandValue::ZeroSized
555 }
556
557 sym::atomic_singlethreadfence => {
558 let ordering = fn_args.const_at(0).to_value();
559 bx.atomic_fence(
560 parse_atomic_ordering(ordering),
561 SynchronizationScope::SingleThread,
562 );
563 OperandValue::ZeroSized
564 }
565
566 sym::nontemporal_store => {
567 let dst = args[0].deref(bx.cx());
568 args[1].val.nontemporal_store(bx, dst);
569 OperandValue::ZeroSized
570 }
571
572 sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
573 let ty = fn_args.type_at(0);
574 let pointee_size = bx.layout_of(ty).size;
575
576 let a = args[0].immediate();
577 let b = args[1].immediate();
578 let a = bx.ptrtoint(a, bx.type_isize());
579 let b = bx.ptrtoint(b, bx.type_isize());
580 let pointee_size = bx.const_usize(pointee_size.bytes());
581 OperandValue::Immediate(if name == sym::ptr_offset_from {
582 let d = bx.sub(a, b);
586 bx.exactsdiv(d, pointee_size)
588 } else {
589 let d = bx.unchecked_usub(a, b);
592 bx.exactudiv(d, pointee_size)
593 })
594 }
595
596 sym::cold_path => {
597 OperandValue::ZeroSized
599 }
600
601 _ => {
602 let result =
604 bx.codegen_intrinsic_call(instance, args, result_layout, result_place, span);
605 if let IntrinsicResult::Operand(op) = result {
606 op
607 } else {
608 return result;
609 }
610 }
611 };
612
613 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!(
614 op_val.is_expected_variant_for_type(result_layout),
615 "[{name:?}] Value {op_val:?} is wrong for type {result_layout:?}",
616 );
617
618 IntrinsicResult::Operand(op_val)
619 }
620}
621
622fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> {
627 match ty.kind() {
628 ty::Int(t) => {
629 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), true))
630 }
631 ty::Uint(t) => {
632 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), false))
633 }
634 _ => None,
635 }
636}
637
638fn float_type_width(ty: Ty<'_>) -> Option<u64> {
641 match ty.kind() {
642 ty::Float(t) => Some(t.bit_width()),
643 _ => None,
644 }
645}