1use rustc_abi::{Align, FieldIdx, WrappingRange};
2use rustc_middle::mir::SourceInfo;
3use rustc_middle::ty::{self, Ty, TyCtxt};
4use rustc_session::config::OptLevel;
5use rustc_span::{ErrorGuaranteed, bug, span_bug, sym};
6use rustc_target::spec::Arch;
7
8use super::operand::{OperandRef, OperandValue};
9use super::place::PlaceValue;
10use super::{FunctionCx, IntrinsicResult};
11use crate::common::{AtomicRmwBinOp, SynchronizationScope};
12use crate::diagnostics::InvalidMonomorphization;
13use crate::mir::operand::OperandRefBuilder;
14use crate::traits::*;
15use crate::{MemFlags, meth, size_of_val};
16
17fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
18 bx: &mut Bx,
19 allow_overlap: bool,
20 volatile: bool,
21 ty: Ty<'tcx>,
22 dst: Bx::Value,
23 src: Bx::Value,
24 count: Bx::Value,
25) {
26 let layout = bx.layout_of(ty);
27 let size = layout.size;
28 let align = layout.align;
29 let size = bx.unchecked_sumul(bx.const_usize(size.bytes()), count);
30 let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() };
31 if allow_overlap {
32 bx.memmove(dst, align, src, align, size, flags);
33 } else {
34 bx.memcpy(dst, align, src, align, size, flags, None);
35 }
36}
37
38fn memset_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
39 bx: &mut Bx,
40 volatile: bool,
41 ty: Ty<'tcx>,
42 dst: Bx::Value,
43 val: Bx::Value,
44 count: Bx::Value,
45) {
46 let layout = bx.layout_of(ty);
47 let size = layout.size;
48 let align = layout.align;
49 let size = bx.mul(bx.const_usize(size.bytes()), count);
50 let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() };
51 bx.memset(dst, val, size, align, flags);
52}
53
54impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
55 pub fn codegen_intrinsic_call(
57 &mut self,
58 bx: &mut Bx,
59 instance: ty::Instance<'tcx>,
60 args: &[OperandRef<'tcx, Bx::Value>],
61 result_layout: ty::layout::TyAndLayout<'tcx>,
62 result_place: Option<PlaceValue<Bx::Value>>,
63 source_info: SourceInfo,
64 ) -> IntrinsicResult<'tcx, Bx::Value> {
65 if bx.tcx().sess.opts.unstable_opts.force_intrinsic_fallback
67 && let Some(def) = bx.tcx().intrinsic(instance.def_id())
68 && !def.must_be_overridden
69 {
70 return IntrinsicResult::Fallback(ty::Instance::new_raw(
71 instance.def_id(),
72 instance.args,
73 ));
74 }
75
76 let span = source_info.span;
77
78 let name = bx.tcx().item_name(instance.def_id());
79 let fn_args = instance.args;
80
81 if let sym::typed_swap_nonoverlapping = name {
85 let pointee_ty = fn_args.type_at(0);
86 let pointee_layout = bx.layout_of(pointee_ty);
87 if pointee_layout.is_ssa_standalone()
88 || bx.sess().opts.optimize == OptLevel::No
91 || bx.sess().target.arch == Arch::SpirV
96 {
97 let align = pointee_layout.align;
98 let x_place = args[0].val.deref(align);
99 let y_place = args[1].val.deref(align);
100 bx.typed_place_swap(x_place, y_place, pointee_layout);
101 return IntrinsicResult::Operand(OperandValue::ZeroSized);
102 }
103 }
104
105 let invalid_monomorphization_int_type = |ty| -> ErrorGuaranteed {
106 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty })
107 };
108 let invalid_monomorphization_int_or_ptr_type = |ty| -> ErrorGuaranteed {
109 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerOrPtrType {
110 span,
111 name,
112 ty,
113 })
114 };
115
116 let parse_atomic_ordering = |ord: ty::Value<'tcx>| {
117 let discr = ord.to_branch()[0].to_leaf();
118 discr.to_atomic_ordering()
119 };
120
121 if args.is_empty() {
122 match name {
123 sym::abort_immediate
124 | sym::unreachable
125 | sym::cold_path
126 | sym::gpu_launch_sized_workgroup_mem
127 | sym::breakpoint
128 | sym::codeview_annotation
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_span::macros::bug_impl(Some(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), Location::caller());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_immediate => {
156 bx.abort_immediate();
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_span::macros::bug_impl(None, format_args!("impossible case reached"),
Location::caller())bug!(),
183 };
184 let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable);
185 match name {
186 sym::vtable_size => {
188 let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
189 bx.range_metadata(value, WrappingRange { start: 0, end: size_bound });
190 }
191 sym::vtable_align => {
194 let align_bound = Align::max_for_target(bx.data_layout()).bytes().into();
195 bx.range_metadata(value, WrappingRange { start: 1, end: align_bound })
196 }
197 _ => {}
198 }
199 OperandValue::Immediate(value)
200 }
201 sym::arith_offset => {
202 let ty = fn_args.type_at(0);
203 let layout = bx.layout_of(ty);
204 let ptr = args[0].immediate();
205 let offset = args[1].immediate();
206 OperandValue::Immediate(bx.gep(layout.size, ptr, offset))
207 }
208 sym::copy => {
209 copy_intrinsic(
210 bx,
211 true,
212 false,
213 fn_args.type_at(0),
214 args[1].immediate(),
215 args[0].immediate(),
216 args[2].immediate(),
217 );
218 OperandValue::ZeroSized
219 }
220 sym::write_bytes => {
221 memset_intrinsic(
222 bx,
223 false,
224 fn_args.type_at(0),
225 args[0].immediate(),
226 args[1].immediate(),
227 args[2].immediate(),
228 );
229 OperandValue::ZeroSized
230 }
231
232 sym::volatile_copy_nonoverlapping_memory => {
233 copy_intrinsic(
234 bx,
235 false,
236 true,
237 fn_args.type_at(0),
238 args[0].immediate(),
239 args[1].immediate(),
240 args[2].immediate(),
241 );
242 OperandValue::ZeroSized
243 }
244 sym::volatile_copy_memory => {
245 copy_intrinsic(
246 bx,
247 true,
248 true,
249 fn_args.type_at(0),
250 args[0].immediate(),
251 args[1].immediate(),
252 args[2].immediate(),
253 );
254 OperandValue::ZeroSized
255 }
256 sym::volatile_set_memory => {
257 memset_intrinsic(
258 bx,
259 true,
260 fn_args.type_at(0),
261 args[0].immediate(),
262 args[1].immediate(),
263 args[2].immediate(),
264 );
265 OperandValue::ZeroSized
266 }
267 sym::volatile_store | sym::unaligned_volatile_store => {
268 let dst = args[0].deref(bx.cx());
269 let dst = if name == sym::volatile_store { dst } else { dst.unaligned() };
270 args[1].val.volatile_store(bx, dst);
271 OperandValue::ZeroSized
272 }
273 sym::disjoint_bitor => {
274 let a = args[0].immediate();
275 let b = args[1].immediate();
276 OperandValue::Immediate(bx.or_disjoint(a, b))
277 }
278 sym::exact_div => {
279 let ty = args[0].layout.ty;
280 match int_type_width_signed(ty, bx.tcx()) {
281 Some((_width, signed)) => OperandValue::Immediate(if signed {
282 bx.exactsdiv(args[0].immediate(), args[1].immediate())
283 } else {
284 bx.exactudiv(args[0].immediate(), args[1].immediate())
285 }),
286 None => {
287 let err = bx
288 .tcx()
289 .dcx()
290 .emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
291 return IntrinsicResult::Err(err);
292 }
293 }
294 }
295 sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
296 match float_type_width(args[0].layout.ty) {
297 Some(_width) => OperandValue::Immediate(match name {
298 sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()),
299 sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()),
300 sym::fmul_fast => bx.fmul_fast(args[0].immediate(), args[1].immediate()),
301 sym::fdiv_fast => bx.fdiv_fast(args[0].immediate(), args[1].immediate()),
302 sym::frem_fast => bx.frem_fast(args[0].immediate(), args[1].immediate()),
303 _ => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
Location::caller())bug!(),
304 }),
305 None => {
306 let err =
307 bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
308 span,
309 name,
310 ty: args[0].layout.ty,
311 });
312 return IntrinsicResult::Err(err);
313 }
314 }
315 }
316 sym::fadd_algebraic
317 | sym::fsub_algebraic
318 | sym::fmul_algebraic
319 | sym::fdiv_algebraic
320 | sym::frem_algebraic => match float_type_width(args[0].layout.ty) {
321 Some(_width) => OperandValue::Immediate(match name {
322 sym::fadd_algebraic => {
323 bx.fadd_algebraic(args[0].immediate(), args[1].immediate())
324 }
325 sym::fsub_algebraic => {
326 bx.fsub_algebraic(args[0].immediate(), args[1].immediate())
327 }
328 sym::fmul_algebraic => {
329 bx.fmul_algebraic(args[0].immediate(), args[1].immediate())
330 }
331 sym::fdiv_algebraic => {
332 bx.fdiv_algebraic(args[0].immediate(), args[1].immediate())
333 }
334 sym::frem_algebraic => {
335 bx.frem_algebraic(args[0].immediate(), args[1].immediate())
336 }
337 _ => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
Location::caller())bug!(),
338 }),
339 None => {
340 let err = bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
341 span,
342 name,
343 ty: args[0].layout.ty,
344 });
345 return IntrinsicResult::Err(err);
346 }
347 },
348
349 sym::float_to_int_unchecked => {
350 if float_type_width(args[0].layout.ty).is_none() {
351 let err =
352 bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
353 span,
354 ty: args[0].layout.ty,
355 });
356 return IntrinsicResult::Err(err);
357 }
358 let Some((_width, signed)) = int_type_width_signed(result_layout.ty, bx.tcx())
359 else {
360 let err =
361 bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
362 span,
363 ty: result_layout.ty,
364 });
365 return IntrinsicResult::Err(err);
366 };
367 OperandValue::Immediate(if signed {
368 bx.fptosi(args[0].immediate(), bx.backend_type(result_layout))
369 } else {
370 bx.fptoui(args[0].immediate(), bx.backend_type(result_layout))
371 })
372 }
373
374 sym::atomic_load => {
375 let ty = fn_args.type_at(0);
376 if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
377 let err = invalid_monomorphization_int_or_ptr_type(ty);
378 return IntrinsicResult::Err(err);
379 }
380 let ordering = fn_args.const_at(1).to_value();
381 let volatile = fn_args.const_at(2).to_value();
382 let layout = bx.layout_of(ty);
383 let source = args[0].immediate();
384 OperandValue::Immediate(bx.atomic_load(
385 bx.backend_type(layout),
386 source,
387 parse_atomic_ordering(ordering),
388 volatile.to_leaf().try_to_bool().unwrap(),
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 volatile = fn_args.const_at(2).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(
404 val,
405 ptr,
406 parse_atomic_ordering(ordering),
407 volatile.to_leaf().try_to_bool().unwrap(),
408 size,
409 );
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
434 let mut builder = OperandRefBuilder::new(result_layout);
435 builder.insert_imm(FieldIdx::from_u32(0), val);
436 builder.insert_imm(FieldIdx::from_u32(1), success);
437 builder.build(bx.cx()).val
438 }
439 sym::atomic_max | sym::atomic_min => {
440 let atom_op = if name == sym::atomic_max {
441 AtomicRmwBinOp::AtomicMax
442 } else {
443 AtomicRmwBinOp::AtomicMin
444 };
445
446 let ty = fn_args.type_at(0);
447 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Int(_) => true,
_ => false,
}matches!(ty.kind(), ty::Int(_)) {
448 let ordering = fn_args.const_at(1).to_value();
449 let ptr = args[0].immediate();
450 let val = args[1].immediate();
451 OperandValue::Immediate(bx.atomic_rmw(
452 atom_op,
453 ptr,
454 val,
455 parse_atomic_ordering(ordering),
456 false,
457 ))
458 } else {
459 let err = invalid_monomorphization_int_type(ty);
460 return IntrinsicResult::Err(err);
461 }
462 }
463 sym::atomic_umax | sym::atomic_umin => {
464 let atom_op = if name == sym::atomic_umax {
465 AtomicRmwBinOp::AtomicUMax
466 } else {
467 AtomicRmwBinOp::AtomicUMin
468 };
469
470 let ty = fn_args.type_at(0);
471 if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Uint(_) => true,
_ => false,
}matches!(ty.kind(), ty::Uint(_)) {
472 let ordering = fn_args.const_at(1).to_value();
473 let ptr = args[0].immediate();
474 let val = args[1].immediate();
475 OperandValue::Immediate(bx.atomic_rmw(
476 atom_op,
477 ptr,
478 val,
479 parse_atomic_ordering(ordering),
480 false,
481 ))
482 } else {
483 let err = invalid_monomorphization_int_type(ty);
484 return IntrinsicResult::Err(err);
485 }
486 }
487 sym::atomic_xchg => {
488 let ty = fn_args.type_at(0);
489 let ordering = fn_args.const_at(1).to_value();
490 if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
491 let ptr = args[0].immediate();
492 let val = args[1].immediate();
493 let atomic_op = AtomicRmwBinOp::AtomicXchg;
494 OperandValue::Immediate(bx.atomic_rmw(
495 atomic_op,
496 ptr,
497 val,
498 parse_atomic_ordering(ordering),
499 ty.is_raw_ptr(),
500 ))
501 } else {
502 let err = invalid_monomorphization_int_or_ptr_type(ty);
503 return IntrinsicResult::Err(err);
504 }
505 }
506 sym::atomic_xadd
507 | sym::atomic_xsub
508 | sym::atomic_and
509 | sym::atomic_nand
510 | sym::atomic_or
511 | sym::atomic_xor => {
512 let atom_op = match name {
513 sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
514 sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
515 sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
516 sym::atomic_nand => AtomicRmwBinOp::AtomicNand,
517 sym::atomic_or => AtomicRmwBinOp::AtomicOr,
518 sym::atomic_xor => AtomicRmwBinOp::AtomicXor,
519 _ => ::core::panicking::unreachable_default_msg()unreachable!(),
520 };
521
522 let ty_mem = fn_args.type_at(0);
524 let ty_op = fn_args.type_at(1);
526
527 let ordering = fn_args.const_at(2).to_value();
528 if (int_type_width_signed(ty_mem, bx.tcx()).is_some() && ty_op == ty_mem)
531 || (ty_mem.is_raw_ptr() && ty_op == bx.tcx().types.usize)
532 {
533 let ptr = args[0].immediate(); let val = args[1].immediate(); OperandValue::Immediate(bx.atomic_rmw(
536 atom_op,
537 ptr,
538 val,
539 parse_atomic_ordering(ordering),
540 ty_mem.is_raw_ptr(),
541 ))
542 } else {
543 let err = invalid_monomorphization_int_or_ptr_type(ty_mem);
544 return IntrinsicResult::Err(err);
545 }
546 }
547 sym::atomic_fence => {
548 let ordering = fn_args.const_at(0).to_value();
549 bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread);
550 OperandValue::ZeroSized
551 }
552
553 sym::atomic_singlethreadfence => {
554 let ordering = fn_args.const_at(0).to_value();
555 bx.atomic_fence(
556 parse_atomic_ordering(ordering),
557 SynchronizationScope::SingleThread,
558 );
559 OperandValue::ZeroSized
560 }
561
562 sym::nontemporal_store => {
563 let dst = args[0].deref(bx.cx());
564 args[1].val.nontemporal_store(bx, dst);
565 OperandValue::ZeroSized
566 }
567
568 sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
569 let ty = fn_args.type_at(0);
570 let pointee_size = bx.layout_of(ty).size;
571
572 let a = args[0].immediate();
573 let b = args[1].immediate();
574 let a = bx.ptrtoint(a, bx.type_isize());
575 let b = bx.ptrtoint(b, bx.type_isize());
576 let pointee_size = bx.const_usize(pointee_size.bytes());
577 OperandValue::Immediate(if name == sym::ptr_offset_from {
578 let d = bx.sub(a, b);
582 bx.exactsdiv(d, pointee_size)
584 } else {
585 let d = bx.unchecked_usub(a, b);
588 bx.exactudiv(d, pointee_size)
589 })
590 }
591
592 sym::cold_path => {
593 OperandValue::ZeroSized
595 }
596
597 _ => {
598 let result =
600 bx.codegen_intrinsic_call(instance, args, result_layout, result_place, span);
601 if let IntrinsicResult::Operand(op) = result {
602 op
603 } else {
604 return result;
605 }
606 }
607 };
608
609 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!(
610 op_val.is_expected_variant_for_type(result_layout),
611 "[{name:?}] Value {op_val:?} is wrong for type {result_layout:?}",
612 );
613
614 IntrinsicResult::Operand(op_val)
615 }
616}
617
618fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> {
623 match ty.kind() {
624 ty::Int(t) => {
625 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), true))
626 }
627 ty::Uint(t) => {
628 Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), false))
629 }
630 _ => None,
631 }
632}
633
634fn float_type_width(ty: Ty<'_>) -> Option<u64> {
637 match ty.kind() {
638 ty::Float(t) => Some(t.bit_width()),
639 _ => None,
640 }
641}