1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![allow(non_upper_case_globals)]
13 use arena
::TypedArena
;
14 use intrinsics
::{self, Intrinsic}
;
17 use llvm
::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeKind}
;
19 use middle
::subst
::FnSpace
;
21 use trans
::attributes
;
26 use trans
::cleanup
::CleanupMethods
;
30 use trans
::debuginfo
::DebugLoc
;
34 use trans
::type_of
::*;
37 use trans
::machine
::llsize_of
;
38 use trans
::type_
::Type
;
39 use middle
::ty
::{self, Ty, HasTypeFlags}
;
40 use middle
::subst
::Substs
;
42 use syntax
::abi
::{self, RustIntrinsic}
;
45 use syntax
::parse
::token
;
47 use std
::cmp
::Ordering
;
49 pub fn get_simple_intrinsic(ccx
: &CrateContext
, item
: &hir
::ForeignItem
) -> Option
<ValueRef
> {
50 let name
= match &*item
.ident
.name
.as_str() {
51 "sqrtf32" => "llvm.sqrt.f32",
52 "sqrtf64" => "llvm.sqrt.f64",
53 "powif32" => "llvm.powi.f32",
54 "powif64" => "llvm.powi.f64",
55 "sinf32" => "llvm.sin.f32",
56 "sinf64" => "llvm.sin.f64",
57 "cosf32" => "llvm.cos.f32",
58 "cosf64" => "llvm.cos.f64",
59 "powf32" => "llvm.pow.f32",
60 "powf64" => "llvm.pow.f64",
61 "expf32" => "llvm.exp.f32",
62 "expf64" => "llvm.exp.f64",
63 "exp2f32" => "llvm.exp2.f32",
64 "exp2f64" => "llvm.exp2.f64",
65 "logf32" => "llvm.log.f32",
66 "logf64" => "llvm.log.f64",
67 "log10f32" => "llvm.log10.f32",
68 "log10f64" => "llvm.log10.f64",
69 "log2f32" => "llvm.log2.f32",
70 "log2f64" => "llvm.log2.f64",
71 "fmaf32" => "llvm.fma.f32",
72 "fmaf64" => "llvm.fma.f64",
73 "fabsf32" => "llvm.fabs.f32",
74 "fabsf64" => "llvm.fabs.f64",
75 "copysignf32" => "llvm.copysign.f32",
76 "copysignf64" => "llvm.copysign.f64",
77 "floorf32" => "llvm.floor.f32",
78 "floorf64" => "llvm.floor.f64",
79 "ceilf32" => "llvm.ceil.f32",
80 "ceilf64" => "llvm.ceil.f64",
81 "truncf32" => "llvm.trunc.f32",
82 "truncf64" => "llvm.trunc.f64",
83 "rintf32" => "llvm.rint.f32",
84 "rintf64" => "llvm.rint.f64",
85 "nearbyintf32" => "llvm.nearbyint.f32",
86 "nearbyintf64" => "llvm.nearbyint.f64",
87 "roundf32" => "llvm.round.f32",
88 "roundf64" => "llvm.round.f64",
89 "ctpop8" => "llvm.ctpop.i8",
90 "ctpop16" => "llvm.ctpop.i16",
91 "ctpop32" => "llvm.ctpop.i32",
92 "ctpop64" => "llvm.ctpop.i64",
93 "bswap16" => "llvm.bswap.i16",
94 "bswap32" => "llvm.bswap.i32",
95 "bswap64" => "llvm.bswap.i64",
96 "assume" => "llvm.assume",
99 Some(ccx
.get_intrinsic(&name
))
102 /// Performs late verification that intrinsics are used correctly. At present,
103 /// the only intrinsic that needs such verification is `transmute`.
104 pub fn check_intrinsics(ccx
: &CrateContext
) {
105 let mut last_failing_id
= None
;
106 for transmute_restriction
in ccx
.tcx().transmute_restrictions
.borrow().iter() {
107 // Sometimes, a single call to transmute will push multiple
108 // type pairs to test in order to exhaustively test the
109 // possibility around a type parameter. If one of those fails,
110 // there is no sense reporting errors on the others.
111 if last_failing_id
== Some(transmute_restriction
.id
) {
115 debug
!("transmute_restriction: {:?}", transmute_restriction
);
117 assert
!(!transmute_restriction
.substituted_from
.has_param_types());
118 assert
!(!transmute_restriction
.substituted_to
.has_param_types());
120 let llfromtype
= type_of
::sizing_type_of(ccx
,
121 transmute_restriction
.substituted_from
);
122 let lltotype
= type_of
::sizing_type_of(ccx
,
123 transmute_restriction
.substituted_to
);
124 let from_type_size
= machine
::llbitsize_of_real(ccx
, llfromtype
);
125 let to_type_size
= machine
::llbitsize_of_real(ccx
, lltotype
);
126 if from_type_size
!= to_type_size
{
127 last_failing_id
= Some(transmute_restriction
.id
);
129 if transmute_restriction
.original_from
!= transmute_restriction
.substituted_from
{
131 transmute_restriction
.span
,
132 &format
!("transmute called on types with potentially different sizes: \
133 {} (could be {} bit{}) to {} (could be {} bit{})",
134 transmute_restriction
.original_from
,
135 from_type_size
as usize,
136 if from_type_size
== 1 {""}
else {"s"}
,
137 transmute_restriction
.original_to
,
138 to_type_size
as usize,
139 if to_type_size
== 1 {""}
else {"s"}
));
142 transmute_restriction
.span
,
143 &format
!("transmute called on types with different sizes: \
144 {} ({} bit{}) to {} ({} bit{})",
145 transmute_restriction
.original_from
,
146 from_type_size
as usize,
147 if from_type_size
== 1 {""}
else {"s"}
,
148 transmute_restriction
.original_to
,
149 to_type_size
as usize,
150 if to_type_size
== 1 {""}
else {"s"}
));
154 ccx
.sess().abort_if_errors();
157 /// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs,
158 /// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics,
159 /// add them to librustc_trans/trans/context.rs
160 pub fn trans_intrinsic_call
<'a
, 'blk
, 'tcx
>(mut bcx
: Block
<'blk
, 'tcx
>,
163 cleanup_scope
: cleanup
::CustomScopeIndex
,
164 args
: callee
::CallArgs
<'a
, 'tcx
>,
166 substs
: subst
::Substs
<'tcx
>,
167 call_info
: NodeIdAndSpan
)
168 -> Result
<'blk
, 'tcx
> {
173 let _icx
= push_ctxt("trans_intrinsic_call");
175 let (arg_tys
, ret_ty
) = match callee_ty
.sty
{
176 ty
::TyBareFn(_
, ref f
) => {
177 (bcx
.tcx().erase_late_bound_regions(&f
.sig
.inputs()),
178 bcx
.tcx().erase_late_bound_regions(&f
.sig
.output()))
180 _
=> panic
!("expected bare_fn in trans_intrinsic_call")
182 let foreign_item
= tcx
.map
.expect_foreign_item(node
);
183 let name
= foreign_item
.ident
.name
.as_str();
185 // For `transmute` we can just trans the input expr directly into dest
186 if name
== "transmute" {
187 let llret_ty
= type_of
::type_of(ccx
, ret_ty
.unwrap());
189 callee
::ArgExprs(arg_exprs
) => {
190 assert_eq
!(arg_exprs
.len(), 1);
192 let (in_type
, out_type
) = (*substs
.types
.get(FnSpace
, 0),
193 *substs
.types
.get(FnSpace
, 1));
194 let llintype
= type_of
::type_of(ccx
, in_type
);
195 let llouttype
= type_of
::type_of(ccx
, out_type
);
197 let in_type_size
= machine
::llbitsize_of_real(ccx
, llintype
);
198 let out_type_size
= machine
::llbitsize_of_real(ccx
, llouttype
);
200 // This should be caught by the intrinsicck pass
201 assert_eq
!(in_type_size
, out_type_size
);
203 let nonpointer_nonaggregate
= |llkind
: TypeKind
| -> bool
{
204 use llvm
::TypeKind
::*;
206 Half
| Float
| Double
| X86_FP80
| FP128
|
207 PPC_FP128
| Integer
| Vector
| X86_MMX
=> true,
212 // An approximation to which types can be directly cast via
213 // LLVM's bitcast. This doesn't cover pointer -> pointer casts,
214 // but does, importantly, cover SIMD types.
215 let in_kind
= llintype
.kind();
216 let ret_kind
= llret_ty
.kind();
217 let bitcast_compatible
=
218 (nonpointer_nonaggregate(in_kind
) && nonpointer_nonaggregate(ret_kind
)) || {
219 in_kind
== TypeKind
::Pointer
&& ret_kind
== TypeKind
::Pointer
222 let dest
= if bitcast_compatible
{
223 // if we're here, the type is scalar-like (a primitive, a
224 // SIMD type or a pointer), and so can be handled as a
225 // by-value ValueRef and can also be directly bitcast to the
226 // target type. Doing this special case makes conversions
227 // like `u32x4` -> `u64x2` much nicer for LLVM and so more
228 // efficient (these are done efficiently implicitly in C
229 // with the `__m128i` type and so this means Rust doesn't
231 let expr
= &*arg_exprs
[0];
232 let datum
= unpack_datum
!(bcx
, expr
::trans(bcx
, expr
));
233 let datum
= unpack_datum
!(bcx
, datum
.to_rvalue_datum(bcx
, "transmute_temp"));
234 let val
= if datum
.kind
.is_by_ref() {
235 load_ty(bcx
, datum
.val
, datum
.ty
)
237 from_arg_ty(bcx
, datum
.val
, datum
.ty
)
240 let cast_val
= BitCast(bcx
, val
, llret_ty
);
244 // this often occurs in a sequence like `Store(val,
245 // d); val2 = Load(d)`, so disappears easily.
246 Store(bcx
, cast_val
, d
);
252 // The types are too complicated to do with a by-value
253 // bitcast, so pointer cast instead. We need to cast the
254 // dest so the types work out.
255 let dest
= match dest
{
256 expr
::SaveIn(d
) => expr
::SaveIn(PointerCast(bcx
, d
, llintype
.ptr_to())),
257 expr
::Ignore
=> expr
::Ignore
259 bcx
= expr
::trans_into(bcx
, &*arg_exprs
[0], dest
);
263 fcx
.scopes
.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
264 fcx
.pop_and_trans_custom_cleanup_scope(bcx
, cleanup_scope
);
267 expr
::SaveIn(d
) => Result
::new(bcx
, d
),
268 expr
::Ignore
=> Result
::new(bcx
, C_undef(llret_ty
.ptr_to()))
274 ccx
.sess().bug("expected expr as argument for transmute");
279 // For `move_val_init` we can evaluate the destination address
280 // (the first argument) and then trans the source value (the
281 // second argument) directly into the resulting destination
283 if name
== "move_val_init" {
284 if let callee
::ArgExprs(ref exprs
) = args
{
285 let (dest_expr
, source_expr
) = if exprs
.len() != 2 {
286 ccx
.sess().bug("expected two exprs as arguments for `move_val_init` intrinsic");
288 (&exprs
[0], &exprs
[1])
291 // evaluate destination address
292 let dest_datum
= unpack_datum
!(bcx
, expr
::trans(bcx
, dest_expr
));
293 let dest_datum
= unpack_datum
!(
294 bcx
, dest_datum
.to_rvalue_datum(bcx
, "arg"));
295 let dest_datum
= unpack_datum
!(
296 bcx
, dest_datum
.to_appropriate_datum(bcx
));
298 // `expr::trans_into(bcx, expr, dest)` is equiv to
300 // `trans(bcx, expr).store_to_dest(dest)`,
302 // which for `dest == expr::SaveIn(addr)`, is equivalent to:
304 // `trans(bcx, expr).store_to(bcx, addr)`.
305 let lldest
= expr
::Dest
::SaveIn(dest_datum
.val
);
306 bcx
= expr
::trans_into(bcx
, source_expr
, lldest
);
308 let llresult
= C_nil(ccx
);
309 fcx
.pop_and_trans_custom_cleanup_scope(bcx
, cleanup_scope
);
311 return Result
::new(bcx
, llresult
);
313 ccx
.sess().bug("expected two exprs as arguments for `move_val_init` intrinsic");
317 let call_debug_location
= DebugLoc
::At(call_info
.id
, call_info
.span
);
319 // For `try` we need some custom control flow
320 if &name
[..] == "try" {
321 if let callee
::ArgExprs(ref exprs
) = args
{
322 let (func
, data
) = if exprs
.len() != 2 {
323 ccx
.sess().bug("expected two exprs as arguments for \
326 (&exprs
[0], &exprs
[1])
329 // translate arguments
330 let func
= unpack_datum
!(bcx
, expr
::trans(bcx
, func
));
331 let func
= unpack_datum
!(bcx
, func
.to_rvalue_datum(bcx
, "func"));
332 let data
= unpack_datum
!(bcx
, expr
::trans(bcx
, data
));
333 let data
= unpack_datum
!(bcx
, data
.to_rvalue_datum(bcx
, "data"));
335 let dest
= match dest
{
336 expr
::SaveIn(d
) => d
,
337 expr
::Ignore
=> alloc_ty(bcx
, tcx
.mk_mut_ptr(tcx
.types
.i8),
342 bcx
= try_intrinsic(bcx
, func
.val
, data
.val
, dest
,
343 call_debug_location
);
345 fcx
.pop_and_trans_custom_cleanup_scope(bcx
, cleanup_scope
);
346 return Result
::new(bcx
, dest
);
348 ccx
.sess().bug("expected two exprs as arguments for \
353 // save the actual AST arguments for later (some places need to do
354 // const-evaluation on them)
355 let expr_arguments
= match args
{
356 callee
::ArgExprs(args
) => Some(args
),
360 // Push the arguments.
361 let mut llargs
= Vec
::new();
362 bcx
= callee
::trans_args(bcx
,
366 cleanup
::CustomScope(cleanup_scope
),
370 fcx
.scopes
.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
372 // These are the only intrinsic functions that diverge.
374 let llfn
= ccx
.get_intrinsic(&("llvm.trap"));
375 Call(bcx
, llfn
, &[], None
, call_debug_location
);
376 fcx
.pop_and_trans_custom_cleanup_scope(bcx
, cleanup_scope
);
378 return Result
::new(bcx
, C_undef(Type
::nil(ccx
).ptr_to()));
379 } else if &name
[..] == "unreachable" {
380 fcx
.pop_and_trans_custom_cleanup_scope(bcx
, cleanup_scope
);
382 return Result
::new(bcx
, C_nil(ccx
));
385 let ret_ty
= match ret_ty
{
386 ty
::FnConverging(ret_ty
) => ret_ty
,
387 ty
::FnDiverging
=> unreachable
!()
390 let llret_ty
= type_of
::type_of(ccx
, ret_ty
);
392 // Get location to store the result. If the user does
393 // not care about the result, just make a stack slot
394 let llresult
= match dest
{
395 expr
::SaveIn(d
) => d
,
397 if !type_is_zero_size(ccx
, ret_ty
) {
398 let llresult
= alloc_ty(bcx
, ret_ty
, "intrinsic_result");
399 call_lifetime_start(bcx
, llresult
);
402 C_undef(llret_ty
.ptr_to())
407 let simple
= get_simple_intrinsic(ccx
, &*foreign_item
);
408 let llval
= match (simple
, &*name
) {
410 Call(bcx
, llfn
, &llargs
, None
, call_debug_location
)
412 (_
, "breakpoint") => {
413 let llfn
= ccx
.get_intrinsic(&("llvm.debugtrap"));
414 Call(bcx
, llfn
, &[], None
, call_debug_location
)
417 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
418 let lltp_ty
= type_of
::type_of(ccx
, tp_ty
);
419 C_uint(ccx
, machine
::llsize_of_alloc(ccx
, lltp_ty
))
421 (_
, "size_of_val") => {
422 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
423 if !type_is_sized(tcx
, tp_ty
) {
424 let (llsize
, _
) = glue
::size_and_align_of_dst(bcx
, tp_ty
, llargs
[1]);
427 let lltp_ty
= type_of
::type_of(ccx
, tp_ty
);
428 C_uint(ccx
, machine
::llsize_of_alloc(ccx
, lltp_ty
))
431 (_
, "min_align_of") => {
432 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
433 C_uint(ccx
, type_of
::align_of(ccx
, tp_ty
))
435 (_
, "min_align_of_val") => {
436 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
437 if !type_is_sized(tcx
, tp_ty
) {
438 let (_
, llalign
) = glue
::size_and_align_of_dst(bcx
, tp_ty
, llargs
[1]);
441 C_uint(ccx
, type_of
::align_of(ccx
, tp_ty
))
444 (_
, "pref_align_of") => {
445 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
446 let lltp_ty
= type_of
::type_of(ccx
, tp_ty
);
447 C_uint(ccx
, machine
::llalign_of_pref(ccx
, lltp_ty
))
449 (_
, "drop_in_place") => {
450 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
451 let ptr
= if type_is_sized(tcx
, tp_ty
) {
454 let scratch
= rvalue_scratch_datum(bcx
, tp_ty
, "tmp");
455 Store(bcx
, llargs
[0], expr
::get_dataptr(bcx
, scratch
.val
));
456 Store(bcx
, llargs
[1], expr
::get_meta(bcx
, scratch
.val
));
457 fcx
.schedule_lifetime_end(cleanup
::CustomScope(cleanup_scope
), scratch
.val
);
460 glue
::drop_ty(bcx
, ptr
, tp_ty
, call_debug_location
);
463 (_
, "type_name") => {
464 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
465 let ty_name
= token
::intern_and_get_ident(&tp_ty
.to_string());
466 C_str_slice(ccx
, ty_name
)
469 let hash
= ccx
.tcx().hash_crate_independent(*substs
.types
.get(FnSpace
, 0),
470 &ccx
.link_meta().crate_hash
);
473 (_
, "init_dropped") => {
474 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
475 if !return_type_is_void(ccx
, tp_ty
) {
476 drop_done_fill_mem(bcx
, llresult
, tp_ty
);
481 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
482 if !return_type_is_void(ccx
, tp_ty
) {
483 // Just zero out the stack slot. (See comment on base::memzero for explanation)
484 init_zero_mem(bcx
, llresult
, tp_ty
);
488 // Effectively no-ops
489 (_
, "uninit") | (_
, "forget") => {
492 (_
, "needs_drop") => {
493 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
495 C_bool(ccx
, bcx
.fcx
.type_needs_drop(tp_ty
))
499 let offset
= llargs
[1];
500 InBoundsGEP(bcx
, ptr
, &[offset
])
502 (_
, "arith_offset") => {
504 let offset
= llargs
[1];
505 GEP(bcx
, ptr
, &[offset
])
508 (_
, "copy_nonoverlapping") => {
512 *substs
.types
.get(FnSpace
, 0),
522 *substs
.types
.get(FnSpace
, 0),
528 (_
, "write_bytes") => {
529 memset_intrinsic(bcx
,
531 *substs
.types
.get(FnSpace
, 0),
538 (_
, "volatile_copy_nonoverlapping_memory") => {
542 *substs
.types
.get(FnSpace
, 0),
548 (_
, "volatile_copy_memory") => {
552 *substs
.types
.get(FnSpace
, 0),
558 (_
, "volatile_set_memory") => {
559 memset_intrinsic(bcx
,
561 *substs
.types
.get(FnSpace
, 0),
567 (_
, "volatile_load") => {
568 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
569 let ptr
= to_arg_ty_ptr(bcx
, llargs
[0], tp_ty
);
570 let load
= VolatileLoad(bcx
, ptr
);
572 llvm
::LLVMSetAlignment(load
, type_of
::align_of(ccx
, tp_ty
));
574 to_arg_ty(bcx
, load
, tp_ty
)
576 (_
, "volatile_store") => {
577 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
578 let ptr
= to_arg_ty_ptr(bcx
, llargs
[0], tp_ty
);
579 let val
= from_arg_ty(bcx
, llargs
[1], tp_ty
);
580 let store
= VolatileStore(bcx
, val
, ptr
);
582 llvm
::LLVMSetAlignment(store
, type_of
::align_of(ccx
, tp_ty
));
587 (_
, "ctlz8") => count_zeros_intrinsic(bcx
,
590 call_debug_location
),
591 (_
, "ctlz16") => count_zeros_intrinsic(bcx
,
594 call_debug_location
),
595 (_
, "ctlz32") => count_zeros_intrinsic(bcx
,
598 call_debug_location
),
599 (_
, "ctlz64") => count_zeros_intrinsic(bcx
,
602 call_debug_location
),
603 (_
, "cttz8") => count_zeros_intrinsic(bcx
,
606 call_debug_location
),
607 (_
, "cttz16") => count_zeros_intrinsic(bcx
,
610 call_debug_location
),
611 (_
, "cttz32") => count_zeros_intrinsic(bcx
,
614 call_debug_location
),
615 (_
, "cttz64") => count_zeros_intrinsic(bcx
,
618 call_debug_location
),
620 (_
, "i8_add_with_overflow") =>
621 with_overflow_intrinsic(bcx
,
622 "llvm.sadd.with.overflow.i8",
626 call_debug_location
),
627 (_
, "i16_add_with_overflow") =>
628 with_overflow_intrinsic(bcx
,
629 "llvm.sadd.with.overflow.i16",
633 call_debug_location
),
634 (_
, "i32_add_with_overflow") =>
635 with_overflow_intrinsic(bcx
,
636 "llvm.sadd.with.overflow.i32",
640 call_debug_location
),
641 (_
, "i64_add_with_overflow") =>
642 with_overflow_intrinsic(bcx
,
643 "llvm.sadd.with.overflow.i64",
647 call_debug_location
),
649 (_
, "u8_add_with_overflow") =>
650 with_overflow_intrinsic(bcx
,
651 "llvm.uadd.with.overflow.i8",
655 call_debug_location
),
656 (_
, "u16_add_with_overflow") =>
657 with_overflow_intrinsic(bcx
,
658 "llvm.uadd.with.overflow.i16",
662 call_debug_location
),
663 (_
, "u32_add_with_overflow") =>
664 with_overflow_intrinsic(bcx
,
665 "llvm.uadd.with.overflow.i32",
669 call_debug_location
),
670 (_
, "u64_add_with_overflow") =>
671 with_overflow_intrinsic(bcx
,
672 "llvm.uadd.with.overflow.i64",
676 call_debug_location
),
677 (_
, "i8_sub_with_overflow") =>
678 with_overflow_intrinsic(bcx
,
679 "llvm.ssub.with.overflow.i8",
683 call_debug_location
),
684 (_
, "i16_sub_with_overflow") =>
685 with_overflow_intrinsic(bcx
,
686 "llvm.ssub.with.overflow.i16",
690 call_debug_location
),
691 (_
, "i32_sub_with_overflow") =>
692 with_overflow_intrinsic(bcx
,
693 "llvm.ssub.with.overflow.i32",
697 call_debug_location
),
698 (_
, "i64_sub_with_overflow") =>
699 with_overflow_intrinsic(bcx
,
700 "llvm.ssub.with.overflow.i64",
704 call_debug_location
),
705 (_
, "u8_sub_with_overflow") =>
706 with_overflow_intrinsic(bcx
,
707 "llvm.usub.with.overflow.i8",
711 call_debug_location
),
712 (_
, "u16_sub_with_overflow") =>
713 with_overflow_intrinsic(bcx
,
714 "llvm.usub.with.overflow.i16",
718 call_debug_location
),
719 (_
, "u32_sub_with_overflow") =>
720 with_overflow_intrinsic(bcx
,
721 "llvm.usub.with.overflow.i32",
725 call_debug_location
),
726 (_
, "u64_sub_with_overflow") =>
727 with_overflow_intrinsic(bcx
,
728 "llvm.usub.with.overflow.i64",
732 call_debug_location
),
733 (_
, "i8_mul_with_overflow") =>
734 with_overflow_intrinsic(bcx
,
735 "llvm.smul.with.overflow.i8",
739 call_debug_location
),
740 (_
, "i16_mul_with_overflow") =>
741 with_overflow_intrinsic(bcx
,
742 "llvm.smul.with.overflow.i16",
746 call_debug_location
),
747 (_
, "i32_mul_with_overflow") =>
748 with_overflow_intrinsic(bcx
,
749 "llvm.smul.with.overflow.i32",
753 call_debug_location
),
754 (_
, "i64_mul_with_overflow") =>
755 with_overflow_intrinsic(bcx
,
756 "llvm.smul.with.overflow.i64",
760 call_debug_location
),
761 (_
, "u8_mul_with_overflow") =>
762 with_overflow_intrinsic(bcx
,
763 "llvm.umul.with.overflow.i8",
767 call_debug_location
),
768 (_
, "u16_mul_with_overflow") =>
769 with_overflow_intrinsic(bcx
,
770 "llvm.umul.with.overflow.i16",
774 call_debug_location
),
775 (_
, "u32_mul_with_overflow") =>
776 with_overflow_intrinsic(bcx
,
777 "llvm.umul.with.overflow.i32",
781 call_debug_location
),
782 (_
, "u64_mul_with_overflow") =>
783 with_overflow_intrinsic(bcx
,
784 "llvm.umul.with.overflow.i64",
788 call_debug_location
),
790 (_
, "unchecked_udiv") => UDiv(bcx
, llargs
[0], llargs
[1], call_debug_location
),
791 (_
, "unchecked_sdiv") => SDiv(bcx
, llargs
[0], llargs
[1], call_debug_location
),
792 (_
, "unchecked_urem") => URem(bcx
, llargs
[0], llargs
[1], call_debug_location
),
793 (_
, "unchecked_srem") => SRem(bcx
, llargs
[0], llargs
[1], call_debug_location
),
795 (_
, "overflowing_add") => Add(bcx
, llargs
[0], llargs
[1], call_debug_location
),
796 (_
, "overflowing_sub") => Sub(bcx
, llargs
[0], llargs
[1], call_debug_location
),
797 (_
, "overflowing_mul") => Mul(bcx
, llargs
[0], llargs
[1], call_debug_location
),
799 (_
, "return_address") => {
800 if !fcx
.caller_expects_out_pointer
{
801 tcx
.sess
.span_err(call_info
.span
,
802 "invalid use of `return_address` intrinsic: function \
803 does not use out pointer");
804 C_null(Type
::i8p(ccx
))
806 PointerCast(bcx
, llvm
::get_param(fcx
.llfn
, 0), Type
::i8p(ccx
))
810 (_
, "discriminant_value") => {
811 let val_ty
= substs
.types
.get(FnSpace
, 0);
814 let repr
= adt
::represent_type(ccx
, *val_ty
);
815 adt
::trans_get_discr(bcx
, &*repr
, llargs
[0], Some(llret_ty
))
817 _
=> C_null(llret_ty
)
820 (_
, name
) if name
.starts_with("simd_") => {
821 generic_simd_intrinsic(bcx
, name
,
830 // This requires that atomic intrinsics follow a specific naming pattern:
831 // "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
832 (_
, name
) if name
.starts_with("atomic_") => {
833 let split
: Vec
<&str> = name
.split('_'
).collect();
834 assert
!(split
.len() >= 2, "Atomic intrinsic not correct format");
836 let order
= if split
.len() == 2 {
837 llvm
::SequentiallyConsistent
840 "unordered" => llvm
::Unordered
,
841 "relaxed" => llvm
::Monotonic
,
842 "acq" => llvm
::Acquire
,
843 "rel" => llvm
::Release
,
844 "acqrel" => llvm
::AcquireRelease
,
845 _
=> ccx
.sess().fatal("unknown ordering in atomic intrinsic")
851 // See include/llvm/IR/Instructions.h for their implementation
852 // of this, I assume that it's good enough for us to use for
854 let strongest_failure_ordering
= match order
{
855 llvm
::NotAtomic
| llvm
::Unordered
=>
856 ccx
.sess().fatal("cmpxchg must be atomic"),
858 llvm
::Monotonic
| llvm
::Release
=>
861 llvm
::Acquire
| llvm
::AcquireRelease
=>
864 llvm
::SequentiallyConsistent
=>
865 llvm
::SequentiallyConsistent
868 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
869 let ptr
= to_arg_ty_ptr(bcx
, llargs
[0], tp_ty
);
870 let cmp
= from_arg_ty(bcx
, llargs
[1], tp_ty
);
871 let src
= from_arg_ty(bcx
, llargs
[2], tp_ty
);
872 let res
= AtomicCmpXchg(bcx
, ptr
, cmp
, src
, order
,
873 strongest_failure_ordering
);
874 ExtractValue(bcx
, res
, 0)
878 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
879 let ptr
= to_arg_ty_ptr(bcx
, llargs
[0], tp_ty
);
880 to_arg_ty(bcx
, AtomicLoad(bcx
, ptr
, order
), tp_ty
)
883 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
884 let ptr
= to_arg_ty_ptr(bcx
, llargs
[0], tp_ty
);
885 let val
= from_arg_ty(bcx
, llargs
[1], tp_ty
);
886 AtomicStore(bcx
, val
, ptr
, order
);
891 AtomicFence(bcx
, order
, llvm
::CrossThread
);
895 "singlethreadfence" => {
896 AtomicFence(bcx
, order
, llvm
::SingleThread
);
900 // These are all AtomicRMW ops
902 let atom_op
= match op
{
903 "xchg" => llvm
::AtomicXchg
,
904 "xadd" => llvm
::AtomicAdd
,
905 "xsub" => llvm
::AtomicSub
,
906 "and" => llvm
::AtomicAnd
,
907 "nand" => llvm
::AtomicNand
,
908 "or" => llvm
::AtomicOr
,
909 "xor" => llvm
::AtomicXor
,
910 "max" => llvm
::AtomicMax
,
911 "min" => llvm
::AtomicMin
,
912 "umax" => llvm
::AtomicUMax
,
913 "umin" => llvm
::AtomicUMin
,
914 _
=> ccx
.sess().fatal("unknown atomic operation")
917 let tp_ty
= *substs
.types
.get(FnSpace
, 0);
918 let ptr
= to_arg_ty_ptr(bcx
, llargs
[0], tp_ty
);
919 let val
= from_arg_ty(bcx
, llargs
[1], tp_ty
);
920 AtomicRMW(bcx
, atom_op
, ptr
, val
, order
)
927 let intr
= match Intrinsic
::find(tcx
, &name
) {
929 None
=> ccx
.sess().span_bug(foreign_item
.span
, "unknown intrinsic"),
931 fn one
<T
>(x
: Vec
<T
>) -> T
{
932 assert_eq
!(x
.len(), 1);
933 x
.into_iter().next().unwrap()
935 fn ty_to_type(ccx
: &CrateContext
, t
: &intrinsics
::Type
,
936 any_changes_needed
: &mut bool
) -> Vec
<Type
> {
937 use intrinsics
::Type
::*;
939 Void
=> vec
![Type
::void(ccx
)],
940 Integer(_signed
, width
, llvm_width
) => {
941 *any_changes_needed
|= width
!= llvm_width
;
942 vec
![Type
::ix(ccx
, llvm_width
as u64)]
946 32 => vec
![Type
::f32(ccx
)],
947 64 => vec
![Type
::f64(ccx
)],
951 Pointer(ref t
, ref llvm_elem
, _const
) => {
952 *any_changes_needed
|= llvm_elem
.is_some();
954 let t
= llvm_elem
.as_ref().unwrap_or(t
);
955 let elem
= one(ty_to_type(ccx
, t
,
956 any_changes_needed
));
959 Vector(ref t
, ref llvm_elem
, length
) => {
960 *any_changes_needed
|= llvm_elem
.is_some();
962 let t
= llvm_elem
.as_ref().unwrap_or(t
);
963 let elem
= one(ty_to_type(ccx
, t
,
964 any_changes_needed
));
965 vec
![Type
::vector(&elem
,
968 Aggregate(false, ref contents
) => {
969 let elems
= contents
.iter()
970 .map(|t
| one(ty_to_type(ccx
, t
, any_changes_needed
)))
971 .collect
::<Vec
<_
>>();
972 vec
![Type
::struct_(ccx
, &elems
, false)]
974 Aggregate(true, ref contents
) => {
975 *any_changes_needed
= true;
977 .flat_map(|t
| ty_to_type(ccx
, t
, any_changes_needed
))
983 // This allows an argument list like `foo, (bar, baz),
984 // qux` to be converted into `foo, bar, baz, qux`, integer
985 // arguments to be truncated as needed and pointers to be
987 fn modify_as_needed
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
988 t
: &intrinsics
::Type
,
994 intrinsics
::Type
::Aggregate(true, ref contents
) => {
995 // We found a tuple that needs squishing! So
996 // run over the tuple and load each field.
998 // This assumes the type is "simple", i.e. no
999 // destructors, and the contents are SIMD
1001 assert
!(!bcx
.fcx
.type_needs_drop(arg_type
));
1003 let repr
= adt
::represent_type(bcx
.ccx(), arg_type
);
1004 let repr_ptr
= &*repr
;
1007 Load(bcx
, adt
::trans_field_ptr(bcx
, repr_ptr
, llarg
, 0, i
))
1011 intrinsics
::Type
::Pointer(_
, Some(ref llvm_elem
), _
) => {
1012 let llvm_elem
= one(ty_to_type(bcx
.ccx(), llvm_elem
, &mut false));
1013 vec
![PointerCast(bcx
, llarg
,
1014 llvm_elem
.ptr_to())]
1016 intrinsics
::Type
::Vector(_
, Some(ref llvm_elem
), length
) => {
1017 let llvm_elem
= one(ty_to_type(bcx
.ccx(), llvm_elem
, &mut false));
1018 vec
![BitCast(bcx
, llarg
,
1019 Type
::vector(&llvm_elem
, length
as u64))]
1021 intrinsics
::Type
::Integer(_
, width
, llvm_width
) if width
!= llvm_width
=> {
1022 // the LLVM intrinsic uses a smaller integer
1023 // size than the C intrinsic's signature, so
1024 // we have to trim it down here.
1025 vec
![Trunc(bcx
, llarg
, Type
::ix(bcx
.ccx(), llvm_width
as u64))]
1032 let mut any_changes_needed
= false;
1033 let inputs
= intr
.inputs
.iter()
1034 .flat_map(|t
| ty_to_type(ccx
, t
, &mut any_changes_needed
))
1035 .collect
::<Vec
<_
>>();
1037 let mut out_changes
= false;
1038 let outputs
= one(ty_to_type(ccx
, &intr
.output
, &mut out_changes
));
1039 // outputting a flattened aggregate is nonsense
1040 assert
!(!out_changes
);
1042 let llargs
= if !any_changes_needed
{
1043 // no aggregates to flatten, so no change needed
1046 // there are some aggregates that need to be flattened
1047 // in the LLVM call, so we need to run over the types
1048 // again to find them and extract the arguments
1052 .flat_map(|((t
, llarg
), ty
)| modify_as_needed(bcx
, t
, ty
, *llarg
))
1055 assert_eq
!(inputs
.len(), llargs
.len());
1057 let val
= match intr
.definition
{
1058 intrinsics
::IntrinsicDef
::Named(name
) => {
1059 let f
= declare
::declare_cfn(ccx
,
1061 Type
::func(&inputs
, &outputs
),
1063 Call(bcx
, f
, &llargs
, None
, call_debug_location
)
1068 intrinsics
::Type
::Aggregate(flatten
, ref elems
) => {
1069 // the output is a tuple so we need to munge it properly
1072 for i
in 0..elems
.len() {
1073 let val
= ExtractValue(bcx
, val
, i
);
1074 Store(bcx
, val
, StructGEP(bcx
, llresult
, i
));
1083 if val_ty(llval
) != Type
::void(ccx
) &&
1084 machine
::llsize_of_alloc(ccx
, val_ty(llval
)) != 0 {
1085 store_ty(bcx
, llval
, llresult
, ret_ty
);
1088 // If we made a temporary stack slot, let's clean it up
1091 bcx
= glue
::drop_ty(bcx
, llresult
, ret_ty
, call_debug_location
);
1092 call_lifetime_end(bcx
, llresult
);
1094 expr
::SaveIn(_
) => {}
1097 fcx
.pop_and_trans_custom_cleanup_scope(bcx
, cleanup_scope
);
1099 Result
::new(bcx
, llresult
)
1102 fn copy_intrinsic
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
1103 allow_overlap
: bool
,
1109 call_debug_location
: DebugLoc
)
1111 let ccx
= bcx
.ccx();
1112 let lltp_ty
= type_of
::type_of(ccx
, tp_ty
);
1113 let align
= C_i32(ccx
, type_of
::align_of(ccx
, tp_ty
) as i32);
1114 let size
= machine
::llsize_of(ccx
, lltp_ty
);
1115 let int_size
= machine
::llbitsize_of_real(ccx
, ccx
.int_type());
1117 let operation
= if allow_overlap
{
1123 let name
= format
!("llvm.{}.p0i8.p0i8.i{}", operation
, int_size
);
1125 let dst_ptr
= PointerCast(bcx
, dst
, Type
::i8p(ccx
));
1126 let src_ptr
= PointerCast(bcx
, src
, Type
::i8p(ccx
));
1127 let llfn
= ccx
.get_intrinsic(&name
);
1133 Mul(bcx
, size
, count
, DebugLoc
::None
),
1135 C_bool(ccx
, volatile
)],
1137 call_debug_location
)
1140 fn memset_intrinsic
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
1146 call_debug_location
: DebugLoc
)
1148 let ccx
= bcx
.ccx();
1149 let lltp_ty
= type_of
::type_of(ccx
, tp_ty
);
1150 let align
= C_i32(ccx
, type_of
::align_of(ccx
, tp_ty
) as i32);
1151 let size
= machine
::llsize_of(ccx
, lltp_ty
);
1152 let int_size
= machine
::llbitsize_of_real(ccx
, ccx
.int_type());
1154 let name
= format
!("llvm.memset.p0i8.i{}", int_size
);
1156 let dst_ptr
= PointerCast(bcx
, dst
, Type
::i8p(ccx
));
1157 let llfn
= ccx
.get_intrinsic(&name
);
1163 Mul(bcx
, size
, count
, DebugLoc
::None
),
1165 C_bool(ccx
, volatile
)],
1167 call_debug_location
)
1170 fn count_zeros_intrinsic(bcx
: Block
,
1173 call_debug_location
: DebugLoc
)
1175 let y
= C_bool(bcx
.ccx(), false);
1176 let llfn
= bcx
.ccx().get_intrinsic(&name
);
1177 Call(bcx
, llfn
, &[val
, y
], None
, call_debug_location
)
1180 fn with_overflow_intrinsic
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
1185 call_debug_location
: DebugLoc
)
1187 let llfn
= bcx
.ccx().get_intrinsic(&name
);
1189 // Convert `i1` to a `bool`, and write it to the out parameter
1190 let val
= Call(bcx
, llfn
, &[a
, b
], None
, call_debug_location
);
1191 let result
= ExtractValue(bcx
, val
, 0);
1192 let overflow
= ZExt(bcx
, ExtractValue(bcx
, val
, 1), Type
::bool(bcx
.ccx()));
1193 Store(bcx
, result
, StructGEP(bcx
, out
, 0));
1194 Store(bcx
, overflow
, StructGEP(bcx
, out
, 1));
1199 fn try_intrinsic
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
1203 dloc
: DebugLoc
) -> Block
<'blk
, 'tcx
> {
1204 if bcx
.sess().no_landing_pads() {
1205 Call(bcx
, func
, &[data
], None
, dloc
);
1206 Store(bcx
, C_null(Type
::i8p(bcx
.ccx())), dest
);
1208 } else if wants_msvc_seh(bcx
.sess()) {
1209 trans_msvc_try(bcx
, func
, data
, dest
, dloc
)
1211 trans_gnu_try(bcx
, func
, data
, dest
, dloc
)
1215 // MSVC's definition of the `rust_try` function. The exact implementation here
1216 // is a little different than the GNU (standard) version below, not only because
1217 // of the personality function but also because of the other fiddly bits about
1218 // SEH. LLVM also currently requires us to structure this a very particular way
1219 // as explained below.
1221 // Like with the GNU version we generate a shim wrapper
1222 fn trans_msvc_try
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
1226 dloc
: DebugLoc
) -> Block
<'blk
, 'tcx
> {
1227 let llfn
= get_rust_try_fn(bcx
.fcx
, &mut |try_fn_ty
, output
| {
1228 let ccx
= bcx
.ccx();
1229 let dloc
= DebugLoc
::None
;
1230 let rust_try
= declare
::define_internal_rust_fn(ccx
, "__rust_try",
1232 let (fcx
, block_arena
);
1233 block_arena
= TypedArena
::new();
1234 fcx
= new_fn_ctxt(ccx
, rust_try
, ast
::DUMMY_NODE_ID
, false,
1235 output
, ccx
.tcx().mk_substs(Substs
::trans_empty()),
1236 None
, &block_arena
);
1237 let bcx
= init_function(&fcx
, true, output
);
1238 let then
= fcx
.new_temp_block("then");
1239 let catch = fcx
.new_temp_block("catch");
1240 let catch_return
= fcx
.new_temp_block("catch-return");
1241 let catch_resume
= fcx
.new_temp_block("catch-resume");
1242 let personality
= fcx
.eh_personality();
1244 let eh_typeid_for
= ccx
.get_intrinsic(&"llvm.eh.typeid.for");
1245 let rust_try_filter
= match bcx
.tcx().lang_items
.msvc_try_filter() {
1246 Some(did
) => callee
::trans_fn_ref(ccx
, did
, ExprId(0),
1247 bcx
.fcx
.param_substs
).val
,
1248 None
=> bcx
.sess().bug("msvc_try_filter not defined"),
1251 // Type indicator for the exception being thrown, not entirely sure
1252 // what's going on here but it's what all the examples in LLVM use.
1253 let lpad_ty
= Type
::struct_(ccx
, &[Type
::i8p(ccx
), Type
::i32(ccx
)],
1256 llvm
::SetFunctionAttribute(rust_try
, llvm
::Attribute
::NoInline
);
1257 llvm
::SetFunctionAttribute(rust_try
, llvm
::Attribute
::OptimizeNone
);
1258 let func
= llvm
::get_param(rust_try
, 0);
1259 let data
= llvm
::get_param(rust_try
, 1);
1261 // Invoke the function, specifying our two temporary landing pads as the
1262 // ext point. After the invoke we've terminated our basic block.
1263 Invoke(bcx
, func
, &[data
], then
.llbb
, catch.llbb
, None
, dloc
);
1265 // All the magic happens in this landing pad, and this is basically the
1266 // only landing pad in rust tagged with "catch" to indicate that we're
1267 // catching an exception. The other catch handlers in the GNU version
1268 // below just catch *all* exceptions, but that's because most exceptions
1269 // are already filtered out by the gnu personality function.
1271 // For MSVC we're just using a standard personality function that we
1272 // can't customize (e.g. _except_handler3 or __C_specific_handler), so
1273 // we need to do the exception filtering ourselves. This is currently
1274 // performed by the `__rust_try_filter` function. This function,
1275 // specified in the landingpad instruction, will be invoked by Windows
1276 // SEH routines and will return whether the exception in question can be
1277 // caught (aka the Rust runtime is the one that threw the exception).
1279 // To get this to compile (currently LLVM segfaults if it's not in this
1280 // particular structure), when the landingpad is executing we test to
1281 // make sure that the ID of the exception being thrown is indeed the one
1282 // that we were expecting. If it's not, we resume the exception, and
1283 // otherwise we return the pointer that we got Full disclosure: It's not
1284 // clear to me what this `llvm.eh.typeid` stuff is doing *other* then
1285 // just allowing LLVM to compile this file without segfaulting. I would
1286 // expect the entire landing pad to just be:
1288 // %vals = landingpad ...
1289 // %ehptr = extractvalue { i8*, i32 } %vals, 0
1292 // but apparently LLVM chokes on this, so we do the more complicated
1293 // thing to placate it.
1294 let vals
= LandingPad(catch, lpad_ty
, personality
, 1);
1295 let rust_try_filter
= BitCast(catch, rust_try_filter
, Type
::i8p(ccx
));
1296 AddClause(catch, vals
, rust_try_filter
);
1297 let ehptr
= ExtractValue(catch, vals
, 0);
1298 let sel
= ExtractValue(catch, vals
, 1);
1299 let filter_sel
= Call(catch, eh_typeid_for
, &[rust_try_filter
], None
,
1301 let is_filter
= ICmp(catch, llvm
::IntEQ
, sel
, filter_sel
, dloc
);
1302 CondBr(catch, is_filter
, catch_return
.llbb
, catch_resume
.llbb
, dloc
);
1304 // Our "catch-return" basic block is where we've determined that we
1305 // actually need to catch this exception, in which case we just return
1306 // the exception pointer.
1307 Ret(catch_return
, ehptr
, dloc
);
1309 // The "catch-resume" block is where we're running this landing pad but
1310 // we actually need to not catch the exception, so just resume the
1311 // exception to return.
1312 Resume(catch_resume
, vals
);
1314 // On the successful branch we just return null.
1315 Ret(then
, C_null(Type
::i8p(ccx
)), dloc
);
1320 // Note that no invoke is used here because by definition this function
1321 // can't panic (that's what it's catching).
1322 let ret
= Call(bcx
, llfn
, &[func
, data
], None
, dloc
);
1323 Store(bcx
, ret
, dest
);
1327 // Definition of the standard "try" function for Rust using the GNU-like model
1328 // of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke
1331 // This translation is a little surprising because
1332 // we always call a shim function instead of inlining the call to `invoke`
1333 // manually here. This is done because in LLVM we're only allowed to have one
1334 // personality per function definition. The call to the `try` intrinsic is
1335 // being inlined into the function calling it, and that function may already
1336 // have other personality functions in play. By calling a shim we're
1337 // guaranteed that our shim will have the right personality function.
1339 fn trans_gnu_try
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
1343 dloc
: DebugLoc
) -> Block
<'blk
, 'tcx
> {
1344 let llfn
= get_rust_try_fn(bcx
.fcx
, &mut |try_fn_ty
, output
| {
1345 let ccx
= bcx
.ccx();
1346 let dloc
= DebugLoc
::None
;
1348 // Translates the shims described above:
1351 // invoke %func(%args...) normal %normal unwind %catch
1357 // (ptr, _) = landingpad
1360 let rust_try
= declare
::define_internal_rust_fn(ccx
, "__rust_try", try_fn_ty
);
1361 attributes
::emit_uwtable(rust_try
, true);
1362 let catch_pers
= match bcx
.tcx().lang_items
.eh_personality_catch() {
1363 Some(did
) => callee
::trans_fn_ref(ccx
, did
, ExprId(0),
1364 bcx
.fcx
.param_substs
).val
,
1365 None
=> bcx
.tcx().sess
.bug("eh_personality_catch not defined"),
1368 let (fcx
, block_arena
);
1369 block_arena
= TypedArena
::new();
1370 fcx
= new_fn_ctxt(ccx
, rust_try
, ast
::DUMMY_NODE_ID
, false,
1371 output
, ccx
.tcx().mk_substs(Substs
::trans_empty()),
1372 None
, &block_arena
);
1373 let bcx
= init_function(&fcx
, true, output
);
1374 let then
= bcx
.fcx
.new_temp_block("then");
1375 let catch = bcx
.fcx
.new_temp_block("catch");
1377 let func
= llvm
::get_param(rust_try
, 0);
1378 let data
= llvm
::get_param(rust_try
, 1);
1379 Invoke(bcx
, func
, &[data
], then
.llbb
, catch.llbb
, None
, dloc
);
1380 Ret(then
, C_null(Type
::i8p(ccx
)), dloc
);
1382 // Type indicator for the exception being thrown.
1383 // The first value in this tuple is a pointer to the exception object being thrown.
1384 // The second value is a "selector" indicating which of the landing pad clauses
1385 // the exception's type had been matched to. rust_try ignores the selector.
1386 let lpad_ty
= Type
::struct_(ccx
, &[Type
::i8p(ccx
), Type
::i32(ccx
)],
1388 let vals
= LandingPad(catch, lpad_ty
, catch_pers
, 1);
1389 AddClause(catch, vals
, C_null(Type
::i8p(ccx
)));
1390 let ptr
= ExtractValue(catch, vals
, 0);
1391 Ret(catch, ptr
, dloc
);
1397 // Note that no invoke is used here because by definition this function
1398 // can't panic (that's what it's catching).
1399 let ret
= Call(bcx
, llfn
, &[func
, data
], None
, dloc
);
1400 Store(bcx
, ret
, dest
);
1404 // Helper to generate the `Ty` associated with `rust_try`
1405 fn get_rust_try_fn
<'a
, 'tcx
>(fcx
: &FunctionContext
<'a
, 'tcx
>,
1406 f
: &mut FnMut(Ty
<'tcx
>,
1407 ty
::FnOutput
<'tcx
>) -> ValueRef
)
1410 if let Some(llfn
) = *ccx
.rust_try_fn().borrow() {
1414 // Define the type up front for the signature of the rust_try function.
1415 let tcx
= ccx
.tcx();
1416 let i8p
= tcx
.mk_mut_ptr(tcx
.types
.i8);
1417 let fn_ty
= tcx
.mk_bare_fn(ty
::BareFnTy
{
1418 unsafety
: hir
::Unsafety
::Unsafe
,
1420 sig
: ty
::Binder(ty
::FnSig
{
1422 output
: ty
::FnOutput
::FnConverging(tcx
.mk_nil()),
1426 let fn_ty
= tcx
.mk_fn(None
, fn_ty
);
1427 let output
= ty
::FnOutput
::FnConverging(i8p
);
1428 let try_fn_ty
= tcx
.mk_bare_fn(ty
::BareFnTy
{
1429 unsafety
: hir
::Unsafety
::Unsafe
,
1431 sig
: ty
::Binder(ty
::FnSig
{
1432 inputs
: vec
![fn_ty
, i8p
],
1437 let rust_try
= f(tcx
.mk_fn(None
, try_fn_ty
), output
);
1438 *ccx
.rust_try_fn().borrow_mut() = Some(rust_try
);
1442 fn generic_simd_intrinsic
<'blk
, 'tcx
, 'a
>
1443 (bcx
: Block
<'blk
, 'tcx
>,
1445 substs
: subst
::Substs
<'tcx
>,
1446 callee_ty
: Ty
<'tcx
>,
1447 args
: Option
<&[P
<hir
::Expr
>]>,
1448 llargs
: &[ValueRef
],
1451 call_debug_location
: DebugLoc
,
1452 call_info
: NodeIdAndSpan
) -> ValueRef
1454 // macros for error handling:
1455 macro_rules
! emit_error
{
1459 ($msg
: tt
, $
($fmt
: tt
)*) => {
1460 bcx
.sess().span_err(call_info
.span
,
1461 &format
!(concat
!("invalid monomorphization of `{}` intrinsic: ",
1466 macro_rules
! require
{
1467 ($cond
: expr
, $
($fmt
: tt
)*) => {
1469 emit_error
!($
($fmt
)*);
1470 return C_null(llret_ty
)
1474 macro_rules
! require_simd
{
1475 ($ty
: expr
, $position
: expr
) => {
1476 require
!($ty
.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position
, $ty
)
1482 let tcx
= bcx
.tcx();
1483 let arg_tys
= match callee_ty
.sty
{
1484 ty
::TyBareFn(_
, ref f
) => {
1485 bcx
.tcx().erase_late_bound_regions(&f
.sig
.inputs())
1490 // every intrinsic takes a SIMD vector as its first argument
1491 require_simd
!(arg_tys
[0], "input");
1492 let in_ty
= arg_tys
[0];
1493 let in_elem
= arg_tys
[0].simd_type(tcx
);
1494 let in_len
= arg_tys
[0].simd_size(tcx
);
1496 let comparison
= match name
{
1497 "simd_eq" => Some(hir
::BiEq
),
1498 "simd_ne" => Some(hir
::BiNe
),
1499 "simd_lt" => Some(hir
::BiLt
),
1500 "simd_le" => Some(hir
::BiLe
),
1501 "simd_gt" => Some(hir
::BiGt
),
1502 "simd_ge" => Some(hir
::BiGe
),
1506 if let Some(cmp_op
) = comparison
{
1507 require_simd
!(ret_ty
, "return");
1509 let out_len
= ret_ty
.simd_size(tcx
);
1510 require
!(in_len
== out_len
,
1511 "expected return type with length {} (same as input type `{}`), \
1512 found `{}` with length {}",
1515 require
!(llret_ty
.element_type().kind() == llvm
::Integer
,
1516 "expected return type with integer elements, found `{}` with non-integer `{}`",
1518 ret_ty
.simd_type(tcx
));
1520 return compare_simd_types(bcx
,
1526 call_debug_location
)
1529 if name
.starts_with("simd_shuffle") {
1530 let n
: usize = match name
["simd_shuffle".len()..].parse() {
1532 Err(_
) => tcx
.sess
.span_bug(call_info
.span
,
1533 "bad `simd_shuffle` instruction only caught in trans?")
1536 require_simd
!(ret_ty
, "return");
1538 let out_len
= ret_ty
.simd_size(tcx
);
1539 require
!(out_len
== n
,
1540 "expected return type of length {}, found `{}` with length {}",
1541 n
, ret_ty
, out_len
);
1542 require
!(in_elem
== ret_ty
.simd_type(tcx
),
1543 "expected return element type `{}` (element of input `{}`), \
1544 found `{}` with element type `{}`",
1546 ret_ty
, ret_ty
.simd_type(tcx
));
1548 let total_len
= in_len
as u64 * 2;
1550 let vector
= match args
{
1551 Some(args
) => &args
[2],
1552 None
=> bcx
.sess().span_bug(call_info
.span
,
1553 "intrinsic call with unexpected argument shape"),
1555 let vector
= consts
::const_expr(bcx
.ccx(), vector
, tcx
.mk_substs(substs
), None
).0;
1557 let indices
: Option
<Vec
<_
>> = (0..n
)
1560 let val
= const_get_elt(bcx
.ccx(), vector
, &[i
as libc
::c_uint
]);
1561 let c
= const_to_opt_uint(val
);
1564 emit_error
!("shuffle index #{} is not a constant", arg_idx
);
1567 Some(idx
) if idx
>= total_len
=> {
1568 emit_error
!("shuffle index #{} is out of bounds (limit {})",
1569 arg_idx
, total_len
);
1572 Some(idx
) => Some(C_i32(bcx
.ccx(), idx
as i32)),
1576 let indices
= match indices
{
1578 None
=> return C_null(llret_ty
)
1581 return ShuffleVector(bcx
, llargs
[0], llargs
[1], C_vector(&indices
))
1584 if name
== "simd_insert" {
1585 require
!(in_elem
== arg_tys
[2],
1586 "expected inserted type `{}` (element of input `{}`), found `{}`",
1587 in_elem
, in_ty
, arg_tys
[2]);
1588 return InsertElement(bcx
, llargs
[0], llargs
[2], llargs
[1])
1590 if name
== "simd_extract" {
1591 require
!(ret_ty
== in_elem
,
1592 "expected return type `{}` (element of input `{}`), found `{}`",
1593 in_elem
, in_ty
, ret_ty
);
1594 return ExtractElement(bcx
, llargs
[0], llargs
[1])
1597 if name
== "simd_cast" {
1598 require_simd
!(ret_ty
, "return");
1599 let out_len
= ret_ty
.simd_size(tcx
);
1600 require
!(in_len
== out_len
,
1601 "expected return type with length {} (same as input type `{}`), \
1602 found `{}` with length {}",
1605 // casting cares about nominal type, not just structural type
1606 let out_elem
= ret_ty
.simd_type(tcx
);
1608 if in_elem
== out_elem { return llargs[0]; }
1610 enum Style { Float, Int(/* is signed? */ bool), Unsupported }
1612 let (in_style
, in_width
) = match in_elem
.sty
{
1613 // vectors of pointer-sized integers should've been
1614 // disallowed before here, so this unwrap is safe.
1615 ty
::TyInt(i
) => (Style
::Int(true), i
.bit_width().unwrap()),
1616 ty
::TyUint(u
) => (Style
::Int(false), u
.bit_width().unwrap()),
1617 ty
::TyFloat(f
) => (Style
::Float
, f
.bit_width()),
1618 _
=> (Style
::Unsupported
, 0)
1620 let (out_style
, out_width
) = match out_elem
.sty
{
1621 ty
::TyInt(i
) => (Style
::Int(true), i
.bit_width().unwrap()),
1622 ty
::TyUint(u
) => (Style
::Int(false), u
.bit_width().unwrap()),
1623 ty
::TyFloat(f
) => (Style
::Float
, f
.bit_width()),
1624 _
=> (Style
::Unsupported
, 0)
1627 match (in_style
, out_style
) {
1628 (Style
::Int(in_is_signed
), Style
::Int(_
)) => {
1629 return match in_width
.cmp(&out_width
) {
1630 Ordering
::Greater
=> Trunc(bcx
, llargs
[0], llret_ty
),
1631 Ordering
::Equal
=> llargs
[0],
1632 Ordering
::Less
=> if in_is_signed
{
1633 SExt(bcx
, llargs
[0], llret_ty
)
1635 ZExt(bcx
, llargs
[0], llret_ty
)
1639 (Style
::Int(in_is_signed
), Style
::Float
) => {
1640 return if in_is_signed
{
1641 SIToFP(bcx
, llargs
[0], llret_ty
)
1643 UIToFP(bcx
, llargs
[0], llret_ty
)
1646 (Style
::Float
, Style
::Int(out_is_signed
)) => {
1647 return if out_is_signed
{
1648 FPToSI(bcx
, llargs
[0], llret_ty
)
1650 FPToUI(bcx
, llargs
[0], llret_ty
)
1653 (Style
::Float
, Style
::Float
) => {
1654 return match in_width
.cmp(&out_width
) {
1655 Ordering
::Greater
=> FPTrunc(bcx
, llargs
[0], llret_ty
),
1656 Ordering
::Equal
=> llargs
[0],
1657 Ordering
::Less
=> FPExt(bcx
, llargs
[0], llret_ty
)
1660 _
=> {/* Unsupported. Fallthrough. */}
1663 "unsupported cast from `{}` with element `{}` to `{}` with element `{}`",
1667 macro_rules
! arith
{
1668 ($
($name
: ident
: $
($
($p
: ident
),* => $call
: expr
),*;)*) => {
1670 if name
== stringify
!($name
) {
1674 return $
call(bcx
, llargs
[0], llargs
[1], call_debug_location
)
1680 "unsupported operation on `{}` with element `{}`",
1687 simd_add
: TyUint
, TyInt
=> Add
, TyFloat
=> FAdd
;
1688 simd_sub
: TyUint
, TyInt
=> Sub
, TyFloat
=> FSub
;
1689 simd_mul
: TyUint
, TyInt
=> Mul
, TyFloat
=> FMul
;
1690 simd_div
: TyFloat
=> FDiv
;
1691 simd_shl
: TyUint
, TyInt
=> Shl
;
1692 simd_shr
: TyUint
=> LShr
, TyInt
=> AShr
;
1693 simd_and
: TyUint
, TyInt
=> And
;
1694 simd_or
: TyUint
, TyInt
=> Or
;
1695 simd_xor
: TyUint
, TyInt
=> Xor
;
1697 bcx
.sess().span_bug(call_info
.span
, "unknown SIMD intrinsic");