Skip to content

Instantly share code, notes, and snippets.

@luqmana
Created December 18, 2015 22:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luqmana/51ac1add2432a258d870 to your computer and use it in GitHub Desktop.
Save luqmana/51ac1add2432a258d870 to your computer and use it in GitHub Desktop.
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,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment