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