mir-intrinsics.diff
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs | |
index 049063f..38fb55f 100644 | |
--- a/src/librustc/mir/repr.rs | |
+++ b/src/librustc/mir/repr.rs | |
@@ -294,7 +294,7 @@ impl<'tcx> Terminator<'tcx> { | |
} | |
} | |
-#[derive(Debug, RustcEncodable, RustcDecodable)] | |
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] | |
pub struct CallData<'tcx> { | |
/// where the return value is written to | |
pub destination: Lvalue<'tcx>, | |
@@ -609,6 +609,8 @@ pub enum CastKind { | |
/// `&[i32;N]` to a `&[i32]`, or a `Box<T>` to a `Box<Trait>` | |
/// (presuming `T: Trait`). | |
Unsize, | |
+ | |
+ Transmute, | |
} | |
#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] | |
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs | |
index 39a315f..39a94e2 100644 | |
--- a/src/librustc_mir/mir_map.rs | |
+++ b/src/librustc_mir/mir_map.rs | |
@@ -145,6 +145,7 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> { | |
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) { | |
Ok(mut mir) => { | |
+ lower_intrinsics::LowerIntrinsics::new(self.tcx).run_on_mir(&mut mir); | |
simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir); | |
let meta_item_list = self.attr | |
diff --git a/src/librustc_mir/transform/lower_intrinsics.rs b/src/librustc_mir/transform/lower_intrinsics.rs | |
new file mode 100644 | |
index 0000000..45da514 | |
--- /dev/null | |
+++ b/src/librustc_mir/transform/lower_intrinsics.rs | |
@@ -0,0 +1,133 @@ | |
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT | |
+// file at the top-level directory of this distribution and at | |
+// http://rust-lang.org/COPYRIGHT. | |
+// | |
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
+// option. This file may not be copied, modified, or distributed | |
+// except according to those terms. | |
+ | |
+//! This pass lowers any Call terminators which are intrinsics | |
+//! to whatever statements or other calls as necessary. | |
+ | |
+use transform::MirPass; | |
+ | |
+use rustc::middle::infer; | |
+use rustc::middle::ty; | |
+use rustc::mir::repr::*; | |
+use rustc::mir::repr::Terminator::Call; | |
+ | |
+use syntax::abi; | |
+ | |
+ | |
+pub struct LowerIntrinsics<'a, 'tcx: 'a> { | |
+ tcx: &'a ty::ctxt<'tcx> | |
+} | |
+ | |
+impl<'a, 'tcx: 'a> LowerIntrinsics<'a, 'tcx> { | |
+ pub fn new(tcx: &'a ty::ctxt<'tcx>) -> LowerIntrinsics<'a, 'tcx> { | |
+ LowerIntrinsics { tcx: tcx } | |
+ } | |
+} | |
+ | |
+impl<'a, 'tcx> MirPass<'tcx> for LowerIntrinsics<'a, 'tcx> { | |
+ fn run_on_mir(&mut self, mir: &mut Mir<'tcx>) { | |
+ let mut bbs = vec![]; | |
+ | |
+ for (i, basic_block) in mir.basic_blocks.iter_mut().enumerate() { | |
+ match basic_block.terminator { | |
+ Call { ref data, .. } if is_intrinsic_call(data) => bbs.push(BasicBlock::new(i)), | |
+ _ => {} | |
+ } | |
+ } | |
+ | |
+ for bb in bbs { | |
+ lower_intrinsic_in_basic_block(self.tcx, mir, bb); | |
+ } | |
+ } | |
+} | |
+ | |
+fn is_intrinsic_call<'tcx>(call_data: &CallData<'tcx>) -> bool { | |
+ if let Operand::Constant(Constant { ty, .. }) = call_data.func { | |
+ match ty.sty { | |
+ ty::TyBareFn(_, fty) if fty.abi == abi::RustIntrinsic => true, | |
+ _ => false | |
+ } | |
+ } else { | |
+ false | |
+ } | |
+} | |
+ | |
+fn lower_intrinsic_in_basic_block<'tcx>(tcx: &ty::ctxt<'tcx>, | |
+ mir: &mut Mir<'tcx>, | |
+ bb: BasicBlock) { | |
+ | |
+ let basic_block = mir.basic_block_data_mut(bb); | |
+ | |
+ let (call_data, targets) = | |
+ if let Call { ref data, targets } = basic_block.terminator { | |
+ ((*data).clone(), targets) | |
+ } else { | |
+ tcx.sess.bug("expected `Call` terminator to lower intrinsic") | |
+ }; | |
+ let (def_id, span, fty) = if let Operand::Constant(Constant { | |
+ span, ty, literal: Literal::Item { def_id, .. } | |
+ }) = call_data.func { | |
+ (def_id, span, ty) | |
+ } else { | |
+ tcx.sess.bug("could not get literal item to lower intrinsic") | |
+ }; | |
+ | |
+ let sig = tcx.erase_late_bound_regions(&fty.fn_sig()); | |
+ let sig = infer::normalize_associated_type(tcx, &sig); | |
+ let ret_ty = sig.output.unwrap_or(tcx.mk_nil()); | |
+ | |
+ let name = &*tcx.item_name(def_id).as_str(); | |
+ | |
+ if name == "transmute" { | |
+ | |
+ assert_eq!(call_data.args.len(), 1); | |
+ | |
+ // dest = transmute(foo) | |
+ // => | |
+ // Assign(dest, Cast(foo)) | |
+ basic_block.statements.push(Statement { | |
+ span: span, | |
+ kind: StatementKind::Assign( | |
+ call_data.destination.clone(), | |
+ Rvalue::Cast(CastKind::Transmute, | |
+ call_data.args[0].clone(), | |
+ ret_ty) | |
+ ) | |
+ }); | |
+ | |
+ // Since this is no longer a function call, replace the | |
+ // terminator with a simple Goto | |
+ basic_block.terminator = Terminator::Goto { target: targets.0 }; | |
+ | |
+ } else if name == "move_val_init" { | |
+ | |
+ assert_eq!(call_data.args.len(), 2); | |
+ | |
+ if let Operand::Consume(dest) = call_data.args[0].clone() { | |
+ // move_val_init(dest, src) | |
+ // => | |
+ // Assign(dest, src) | |
+ basic_block.statements.push(Statement { | |
+ span: span, | |
+ kind: StatementKind::Assign( | |
+ dest, | |
+ Rvalue::Use(call_data.args[1].clone()) | |
+ ) | |
+ }); | |
+ } else { | |
+ tcx.sess.span_bug(span, "destination argument not lvalue?"); | |
+ } | |
+ | |
+ // Since this is no longer a function call, replace the | |
+ // terminator with a simple Goto | |
+ basic_block.terminator = Terminator::Goto { target: targets.0 }; | |
+ | |
+ } | |
+} | |
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs | |
index 174718f..6b37e01 100644 | |
--- a/src/librustc_mir/transform/mod.rs | |
+++ b/src/librustc_mir/transform/mod.rs | |
@@ -10,6 +10,7 @@ | |
pub mod simplify_cfg; | |
pub mod erase_regions; | |
+pub mod lower_intrinsics; | |
mod util; | |
use rustc::mir::repr::Mir; | |
diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs | |
index 529e65d..1dc14f2 100644 | |
--- a/src/librustc_trans/trans/mir/rvalue.rs | |
+++ b/src/librustc_trans/trans/mir/rvalue.rs | |
@@ -8,13 +8,13 @@ | |
// option. This file may not be copied, modified, or distributed | |
// except according to those terms. | |
-use llvm::ValueRef; | |
+use llvm::{TypeKind, ValueRef}; | |
use rustc::middle::ty::{self, Ty}; | |
use rustc::mir::repr as mir; | |
use trans::asm; | |
use trans::base; | |
-use trans::build; | |
+use trans::build::{self, BitCast, IntToPtr, PointerCast, PtrToInt}; | |
use trans::common::{self, Block, Result}; | |
use trans::debuginfo::DebugLoc; | |
use trans::declare; | |
@@ -140,6 +140,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { | |
{ | |
assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); | |
+ let ccx = bcx.ccx(); | |
+ let tcx = bcx.tcx(); | |
match *rvalue { | |
mir::Rvalue::Use(ref operand) => { | |
let operand = self.trans_operand(bcx, operand); | |
@@ -185,7 +187,86 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { | |
} | |
} | |
} | |
- mir::CastKind::Misc => unimplemented!() | |
+ mir::CastKind::Misc => unimplemented!(), | |
+ mir::CastKind::Transmute => { | |
+ use trans::common::{type_is_fat_ptr, type_is_immediate}; | |
+ | |
+ let llval_ty = type_of::type_of(bcx.ccx(), operand.ty); | |
+ let llcast_ty = type_of::type_of(bcx.ccx(), cast_ty); | |
+ | |
+ assert_eq!(machine::llbitsize_of_real(bcx.ccx(), llval_ty), | |
+ machine::llbitsize_of_real(bcx.ccx(), llcast_ty)); | |
+ | |
+ let nonpointer_nonaggregate = |llkind: TypeKind| -> bool { | |
+ use llvm::TypeKind::*; | |
+ match llkind { | |
+ Half | Float | Double | X86_FP80 | FP128 | | |
+ PPC_FP128 | Integer | Vector | X86_MMX => true, | |
+ _ => false | |
+ } | |
+ }; | |
+ | |
+ let val_kind = llval_ty.kind(); | |
+ let cast_kind = llcast_ty.kind(); | |
+ | |
+ match operand.val { | |
+ // Cast Ref => Fat Ptr | |
+ // ex: (*const T, usize) => &[T] | |
+ OperandValue::Ref(r) if type_is_fat_ptr(tcx, cast_ty) => { | |
+ let (data, extra) = base::load_fat_ptr(bcx, r, cast_ty); | |
+ OperandValue::FatPtr(data, extra) | |
+ }, | |
+ | |
+ // Cast Ref => Ref | |
+ // ex: &StructA => &StructB | |
+ OperandValue::Ref(r) => { | |
+ assert!(!type_is_immediate(ccx, cast_ty)); | |
+ OperandValue::Ref(PointerCast(bcx, r, llcast_ty.ptr_to())) | |
+ }, | |
+ | |
+ // Cast Immediate => Immediate | |
+ OperandValue::Immediate(i) => { | |
+ assert!(type_is_immediate(ccx, cast_ty)); | |
+ let val = match (val_kind, cast_kind) { | |
+ (TypeKind::Pointer, TypeKind::Integer) => | |
+ PtrToInt(bcx, i, llcast_ty), | |
+ | |
+ (TypeKind::Integer, TypeKind::Pointer) => | |
+ IntToPtr(bcx, i, llcast_ty), | |
+ | |
+ (TypeKind::Pointer, TypeKind::Pointer) => | |
+ PointerCast(bcx, i, llcast_ty), | |
+ | |
+ _ if nonpointer_nonaggregate(val_kind) && | |
+ nonpointer_nonaggregate(cast_kind) => { | |
+ BitCast(bcx, i, llcast_ty) | |
+ }, | |
+ | |
+ _ => { | |
+ let lltemp = base::alloc_ty(bcx, cast_ty, "__transmute_temp"); | |
+ let lldest = PointerCast(bcx, lltemp, llval_ty.ptr_to()); | |
+ base::store_ty(bcx, i, lldest, operand.ty); | |
+ base::load_ty(bcx, lltemp, cast_ty) | |
+ } | |
+ }; | |
+ OperandValue::Immediate(val) | |
+ }, | |
+ | |
+ // Fat Ptr => Fat Ptr | |
+ OperandValue::FatPtr(_, _) if type_is_fat_ptr(tcx, cast_ty) | |
+ => operand.val, | |
+ | |
+ // Fat Ptr => Ref | |
+ // ex: &[T] => (*const T, usize) | |
+ OperandValue::FatPtr(d, e) => { | |
+ assert!(!type_is_immediate(ccx, cast_ty)); | |
+ let lltemp = base::alloc_ty(bcx, cast_ty, "__transmute_temp"); | |
+ let lldest = PointerCast(bcx, lltemp, llval_ty.ptr_to()); | |
+ base::store_fat_ptr(bcx, d, e, lldest, operand.ty); | |
+ OperandValue::Ref(lltemp) | |
+ } | |
+ } | |
+ } | |
}; | |
(bcx, OperandRef { | |
val: val, |