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);
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);
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 layout = bx.layout_of(ty);
385 let source = args[0].immediate();
386 OperandValue::Immediate(bx.atomic_load(
387 bx.backend_type(layout),
388 source,
389 parse_atomic_ordering(ordering),
390 layout.size,
391 ))
392 }
393 sym::atomic_store => {
394 let ty = fn_args.type_at(0);
395 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
396 let err = invalid_monomorphization_int_or_ptr_type(ty);
397 return IntrinsicResult::Err(err);
398 }
399 let ordering = fn_args.const_at(1).to_value();
400 let size = bx.layout_of(ty).size;
401 let val = args[1].immediate();
402 let ptr = args[0].immediate();
403 bx.atomic_store(val, ptr, parse_atomic_ordering(ordering), size);
404 OperandValue::ZeroSized
405 }
406 sym::atomic_cxchg | sym::atomic_cxchgweak => {
408 let ty = fn_args.type_at(0);
409 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
410 let err = invalid_monomorphization_int_or_ptr_type(ty);
411 return IntrinsicResult::Err(err);
412 }
413 let succ_ordering = fn_args.const_at(1).to_value();
414 let fail_ordering = fn_args.const_at(2).to_value();
415 let weak = name == sym::atomic_cxchgweak;
416 let dst = args[0].immediate();
417 let cmp = args[1].immediate();
418 let src = args[2].immediate();
419 let (val, success) = bx.atomic_cmpxchg(
420 dst,
421 cmp,
422 src,
423 parse_atomic_ordering(succ_ordering),
424 parse_atomic_ordering(fail_ordering),
425 weak,
426 );
427 let val = bx.from_immediate(val);
428 let success = bx.from_immediate(success);
429
430 let mut builder = OperandRefBuilder::new(result_layout);
431 builder.insert_imm(FieldIdx::from_u32(0), val);
432 builder.insert_imm(FieldIdx::from_u32(1), success);
433 builder.build(bx.cx()).val
434 }
435 sym::atomic_max | sym::atomic_min => {
436 let atom_op = if name == sym::atomic_max {
437 AtomicRmwBinOp::AtomicMax
438 } else {
439 AtomicRmwBinOp::AtomicMin
440 };
441
442 let ty = fn_args.type_at(0);
443 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Int(_) => true,
_ => false,
}matches!(ty.kind(), ty::Int(_)) {
444 let ordering = fn_args.const_at(1).to_value();
445 let ptr = args[0].immediate();
446 let val = args[1].immediate();
447 OperandValue::Immediate(bx.atomic_rmw(
448 atom_op,
449 ptr,
450 val,
451 parse_atomic_ordering(ordering),
452 false,
453 ))
454 } else {
455 let err = invalid_monomorphization_int_type(ty);
456 return IntrinsicResult::Err(err);
457 }
458 }
459 sym::atomic_umax | sym::atomic_umin => {
460 let atom_op = if name == sym::atomic_umax {
461 AtomicRmwBinOp::AtomicUMax
462 } else {
463 AtomicRmwBinOp::AtomicUMin
464 };
465
466 let ty = fn_args.type_at(0);
467 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Uint(_) => true,
_ => false,
}matches!(ty.kind(), ty::Uint(_)) {
468 let ordering = fn_args.const_at(1).to_value();
469 let ptr = args[0].immediate();
470 let val = args[1].immediate();
471 OperandValue::Immediate(bx.atomic_rmw(
472 atom_op,
473 ptr,
474 val,
475 parse_atomic_ordering(ordering),
476 false,
477 ))
478 } else {
479 let err = invalid_monomorphization_int_type(ty);
480 return IntrinsicResult::Err(err);
481 }
482 }
483 sym::atomic_xchg => {
484 let ty = fn_args.type_at(0);
485 let ordering = fn_args.const_at(1).to_value();
486 if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
487 let ptr = args[0].immediate();
488 let val = args[1].immediate();
489 let atomic_op = AtomicRmwBinOp::AtomicXchg;
490 OperandValue::Immediate(bx.atomic_rmw(
491 atomic_op,
492 ptr,
493 val,
494 parse_atomic_ordering(ordering),
495 ty.is_raw_ptr(),
496 ))
497 } else {
498 let err = invalid_monomorphization_int_or_ptr_type(ty);
499 return IntrinsicResult::Err(err);
500 }
501 }
502 sym::atomic_xadd
503 | sym::atomic_xsub
504 | sym::atomic_and
505 | sym::atomic_nand
506 | sym::atomic_or
507 | sym::atomic_xor => {
508 let atom_op = match name {
509 sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
510 sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
511 sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
512 sym::atomic_nand => AtomicRmwBinOp::AtomicNand,
513 sym::atomic_or => AtomicRmwBinOp::AtomicOr,
514 sym::atomic_xor => AtomicRmwBinOp::AtomicXor,
515 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
516 };
517
518 let ty_mem = fn_args.type_at(0);
520 let ty_op = fn_args.type_at(1);
522
523 let ordering = fn_args.const_at(2).to_value();
524 if (int_type_width_signed(ty_mem, bx.tcx()).is_some() && ty_op == ty_mem)
527 || (ty_mem.is_raw_ptr() && ty_op == bx.tcx().types.usize)
528 {
529 let ptr = args[0].immediate(); let val = args[1].immediate(); OperandValue::Immediate(bx.atomic_rmw(
532 atom_op,
533 ptr,
534 val,
535 parse_atomic_ordering(ordering),
536 ty_mem.is_raw_ptr(),
537 ))
538 } else {
539 let err = invalid_monomorphization_int_or_ptr_type(ty_mem);
540 return IntrinsicResult::Err(err);
541 }
542 }
543 sym::atomic_fence => {
544 let ordering = fn_args.const_at(0).to_value();
545 bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread);
546 OperandValue::ZeroSized
547 }
548
549 sym::atomic_singlethreadfence => {
550 let ordering = fn_args.const_at(0).to_value();
551 bx.atomic_fence(
552 parse_atomic_ordering(ordering),
553 SynchronizationScope::SingleThread,
554 );
555 OperandValue::ZeroSized
556 }
557
558 sym::nontemporal_store => {
559 let dst = args[0].deref(bx.cx());
560 args[1].val.nontemporal_store(bx, dst);
561 OperandValue::ZeroSized
562 }
563
564 sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
565 let ty = fn_args.type_at(0);
566 let pointee_size = bx.layout_of(ty).size;
567
568 let a = args[0].immediate();
569 let b = args[1].immediate();
570 let a = bx.ptrtoint(a, bx.type_isize());
571 let b = bx.ptrtoint(b, bx.type_isize());
572 let pointee_size = bx.const_usize(pointee_size.bytes());
573 OperandValue::Immediate(if name == sym::ptr_offset_from {
574 let d = bx.sub(a, b);
578 bx.exactsdiv(d, pointee_size)
580 } else {
581 let d = bx.unchecked_usub(a, b);
584 bx.exactudiv(d, pointee_size)
585 })
586 }
587
588 sym::cold_path => {
589 OperandValue::ZeroSized
591 }
592
593 _ => {
594 let result =
596 bx.codegen_intrinsic_call(instance, args, result_layout, result_place, span);
597 if let IntrinsicResult::Operand(op) = result {
598 op
599 } else {
600 return result;
601 }
602 }
603 };
604
605 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!(
606 op_val.is_expected_variant_for_type(result_layout),
607 "[{name:?}] Value {op_val:?} is wrong for type {result_layout:?}",
608 );
609
610 IntrinsicResult::Operand(op_val)
611 }
612}
613
614fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> {
619 match ty.kind() {
620 ty::Int(t) => {
621 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), true))
622 }
623 ty::Uint(t) => {
624 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), false))
625 }
626 _ => None,
627 }
628}
629
630fn float_type_width(ty: Ty<'_>) -> Option<u64> {
633 match ty.kind() {
634 ty::Float(t) => Some(t.bit_width()),
635 _ => None,
636 }
637}