From 02017b30ebebfaeb64a5a86a885773f38057beba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:42:25 -0400 Subject: [PATCH] New trans codepath that builds fn body from MIR instead. --- src/librustc_trans/trans/common.rs | 6 + src/librustc_trans/trans/mir/block.rs | 106 ++++++++++ src/librustc_trans/trans/mir/constant.rs | 63 ++++++ src/librustc_trans/trans/mir/lvalue.rs | 149 ++++++++++++++ src/librustc_trans/trans/mir/mod.rs | 162 +++++++++++++++ src/librustc_trans/trans/mir/operand.rs | 90 ++++++++ src/librustc_trans/trans/mir/rvalue.rs | 238 ++++++++++++++++++++++ src/librustc_trans/trans/mir/statement.rs | 48 +++++ src/librustc_trans/trans/mod.rs | 1 + src/test/run-pass/mir_trans_spike1.rs | 24 +++ 10 files changed, 887 insertions(+) create mode 100644 src/librustc_trans/trans/mir/block.rs create mode 100644 src/librustc_trans/trans/mir/constant.rs create mode 100644 src/librustc_trans/trans/mir/lvalue.rs create mode 100644 src/librustc_trans/trans/mir/mod.rs create mode 100644 src/librustc_trans/trans/mir/operand.rs create mode 100644 src/librustc_trans/trans/mir/rvalue.rs create mode 100644 src/librustc_trans/trans/mir/statement.rs create mode 100644 src/test/run-pass/mir_trans_spike1.rs diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index b5f192b97274..8d6ba53dd222 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -743,6 +743,12 @@ pub fn C_floating(s: &str, t: Type) -> ValueRef { } } +pub fn C_floating_f64(f: f64, t: Type) -> ValueRef { + unsafe { + llvm::LLVMConstReal(t.to_ref(), f) + } +} + pub fn C_nil(ccx: &CrateContext) -> ValueRef { C_struct(ccx, &[], false) } diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs new file mode 100644 index 000000000000..c9f4123c1712 --- /dev/null +++ b/src/librustc_trans/trans/mir/block.rs @@ -0,0 +1,106 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::BasicBlockRef; +use rustc_mir::repr as mir; +use trans::base; +use trans::build; +use trans::common::Block; +use trans::debuginfo::DebugLoc; + +use super::MirContext; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_block(&mut self, bb: mir::BasicBlock) { + debug!("trans_block({:?})", bb); + + let mut bcx = self.bcx(bb); + let data = self.mir.basic_block_data(bb); + + for statement in &data.statements { + bcx = self.trans_statement(bcx, statement); + } + + debug!("trans_block: terminator: {:?}", data.terminator); + + match data.terminator { + mir::Terminator::Goto { target } => { + build::Br(bcx, self.llblock(target), DebugLoc::None) + } + + mir::Terminator::Panic { .. } => { + unimplemented!() + } + + mir::Terminator::If { ref cond, targets: [true_bb, false_bb] } => { + let cond = self.trans_operand(bcx, cond); + let lltrue = self.llblock(true_bb); + let llfalse = self.llblock(false_bb); + build::CondBr(bcx, cond.llval, lltrue, llfalse, DebugLoc::None); + } + + mir::Terminator::Switch { .. } => { + unimplemented!() + } + + mir::Terminator::Diverge => { + if let Some(llpersonalityslot) = self.llpersonalityslot { + let lp = build::Load(bcx, llpersonalityslot); + // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality); + build::Resume(bcx, lp); + } else { + // This fn never encountered anything fallible, so + // a Diverge cannot actually happen. Note that we + // do a total hack to ensure that we visit the + // DIVERGE block last. + build::Unreachable(bcx); + } + } + + mir::Terminator::Return => { + let return_ty = bcx.monomorphize(&self.mir.return_ty); + base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None); + } + + mir::Terminator::Call { .. } => { + unimplemented!() + //let llbb = unimplemented!(); // self.make_landing_pad(panic_bb); + // + //let tr_dest = self.trans_lvalue(bcx, &data.destination); + // + //// Create the callee. This will always be a fn + //// ptr and hence a kind of scalar. + //let callee = self.trans_operand(bcx, &data.func); + // + //// Process the arguments. + // + //let args = unimplemented!(); + // + //callee::trans_call_inner(bcx, + // DebugLoc::None, + // |bcx, _| Callee { + // bcx: bcx, + // data: CalleeData::Fn(callee.llval), + // ty: callee.ty, + // }, + // args, + // Some(Dest::SaveIn(tr_dest.llval))); + } + } + } + + fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> { + self.blocks[bb.index()] + } + + fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef { + self.blocks[bb.index()].llbb + } +} diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs new file mode 100644 index 000000000000..1b61001834a9 --- /dev/null +++ b/src/librustc_trans/trans/mir/constant.rs @@ -0,0 +1,63 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::ValueRef; +use rustc::middle::const_eval::ConstVal; +use rustc_mir::repr as mir; +use trans::consts::{self, TrueConst}; +use trans::common::{self, Block}; +use trans::type_of; + +use super::MirContext; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_constant(&mut self, + bcx: Block<'bcx, 'tcx>, + constant: &mir::Constant<'tcx>) + -> ValueRef + { + let ccx = bcx.ccx(); + let constant_ty = bcx.monomorphize(&constant.ty); + let llty = type_of::type_of(ccx, constant_ty); + match constant.literal { + mir::Literal::Item { .. } => { + unimplemented!() + } + mir::Literal::Value { ref value } => { + match *value { + ConstVal::Float(v) => common::C_floating_f64(v, llty), + ConstVal::Bool(v) => common::C_bool(ccx, v), + ConstVal::Int(v) => common::C_integral(llty, v as u64, true), + ConstVal::Uint(v) => common::C_integral(llty, v, false), + ConstVal::Str(ref v) => common::C_str_slice(ccx, v.clone()), + ConstVal::ByteStr(ref v) => consts::addr_of(ccx, + common::C_bytes(ccx, v), + 1, + "byte_str"), + ConstVal::Struct(id) | ConstVal::Tuple(id) => { + let expr = bcx.tcx().map.expect_expr(id); + let (llval, _) = match consts::const_expr(ccx, + expr, + bcx.fcx.param_substs, + None, + TrueConst::Yes) { + Ok(v) => v, + Err(_) => panic!("constant eval failure"), + }; + llval + } + ConstVal::Function(_) => { + unimplemented!() + } + } + } + } + } +} diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs new file mode 100644 index 000000000000..282c6a7e6de1 --- /dev/null +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -0,0 +1,149 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_mir::repr as mir; +use rustc_mir::tcx::LvalueTy; +use trans::adt; +use trans::base; +use trans::build; +use trans::common::{self, Block}; +use trans::debuginfo::DebugLoc; +use trans::machine; +use trans::tvec; + +use super::MirContext; + +#[derive(Copy, Clone)] +pub struct LvalueRef<'tcx> { + /// Pointer to the contents of the lvalue + pub llval: ValueRef, + + /// Monomorphized type of this lvalue, including variant information + pub ty: LvalueTy<'tcx>, +} + +impl<'tcx> LvalueRef<'tcx> { + pub fn new(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> { + LvalueRef { llval: llval, ty: lvalue_ty } + } + + pub fn alloca<'bcx>(bcx: Block<'bcx, 'tcx>, + ty: Ty<'tcx>, + name: &str) + -> LvalueRef<'tcx> + { + let lltemp = base::alloc_ty(bcx, ty, name); + LvalueRef::new(lltemp, LvalueTy::from_ty(ty)) + } +} + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_lvalue(&mut self, + bcx: Block<'bcx, 'tcx>, + lvalue: &mir::Lvalue<'tcx>) + -> LvalueRef<'tcx> { + debug!("trans_lvalue(lvalue={:?})", lvalue); + + let fcx = bcx.fcx; + let ccx = fcx.ccx; + let tcx = bcx.tcx(); + match *lvalue { + mir::Lvalue::Var(index) => self.vars[index as usize], + mir::Lvalue::Temp(index) => self.temps[index as usize], + mir::Lvalue::Arg(index) => self.args[index as usize], + mir::Lvalue::Static(_def_id) => unimplemented!(), + mir::Lvalue::ReturnPointer => { + let return_ty = bcx.monomorphize(&self.mir.return_ty); + let llval = fcx.get_ret_slot(bcx, return_ty, "return"); + LvalueRef::new(llval, LvalueTy::from_ty(return_ty.unwrap())) + } + mir::Lvalue::Projection(ref projection) => { + let tr_base = self.trans_lvalue(bcx, &projection.base); + let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); + let llprojected = match projection.elem { + mir::ProjectionElem::Deref => { + let base_ty = tr_base.ty.to_ty(tcx); + base::load_ty(bcx, tr_base.llval, base_ty) + } + mir::ProjectionElem::Field(ref field) => { + let base_ty = tr_base.ty.to_ty(tcx); + let base_repr = adt::represent_type(ccx, base_ty); + let discr = match tr_base.ty { + LvalueTy::Ty { .. } => 0, + LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v, + }; + let discr = discr as u64; + adt::trans_field_ptr(bcx, &base_repr, tr_base.llval, discr, field.index()) + } + mir::ProjectionElem::Index(ref index) => { + let base_ty = tr_base.ty.to_ty(tcx); + let index = self.trans_operand(bcx, index); + let llindex = self.prepare_index(bcx, index.llval); + let (llbase, _) = tvec::get_base_and_len(bcx, tr_base.llval, base_ty); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: false, + min_length: _ } => { + let base_ty = tr_base.ty.to_ty(tcx); + let lloffset = common::C_u32(bcx.ccx(), offset); + let llindex = self.prepare_index(bcx, lloffset); + let (llbase, _) = tvec::get_base_and_len(bcx, + tr_base.llval, + base_ty); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: true, + min_length: _ } => { + let lloffset = common::C_u32(bcx.ccx(), offset); + let base_ty = tr_base.ty.to_ty(tcx); + let (llbase, lllen) = tvec::get_base_and_len(bcx, + tr_base.llval, + base_ty); + let llindex = build::Sub(bcx, lllen, lloffset, DebugLoc::None); + let llindex = self.prepare_index(bcx, llindex); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::Downcast(..) => { + tr_base.llval + } + }; + LvalueRef { + llval: llprojected, + ty: projected_ty, + } + } + } + } + + /// Adjust the bitwidth of an index since LLVM is less forgiving + /// than we are. + /// + /// nmatsakis: is this still necessary? Not sure. + fn prepare_index(&mut self, + bcx: Block<'bcx, 'tcx>, + llindex: ValueRef) + -> ValueRef + { + let ccx = bcx.ccx(); + let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex)); + let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type()); + if index_size < int_size { + build::ZExt(bcx, llindex, ccx.int_type()) + } else if index_size > int_size { + build::Trunc(bcx, llindex, ccx.int_type()) + } else { + llindex + } + } +} diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs new file mode 100644 index 000000000000..6ed839d1a442 --- /dev/null +++ b/src/librustc_trans/trans/mir/mod.rs @@ -0,0 +1,162 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use libc::c_uint; +use llvm::{self, ValueRef}; +use rustc_mir::repr as mir; +use rustc_mir::tcx::LvalueTy; +use std::cell::Cell; +use trans::base; +use trans::build; +use trans::common::{self, Block}; +use trans::debuginfo::DebugLoc; +use trans::expr; +use trans::type_of; + +use self::lvalue::LvalueRef; + +// FIXME DebugLoc is always None right now + +/// Master context for translating MIR. +pub struct MirContext<'bcx, 'tcx:'bcx> { + mir: &'bcx mir::Mir<'tcx>, + + /// When unwinding is initiated, we have to store this personality + /// value somewhere so that we can load it and re-use it in the + /// resume instruction. The personality is (afaik) some kind of + /// value used for C++ unwinding, which must filter by type: we + /// don't really care about it very much. Anyway, this value + /// contains an alloca into which the personality is stored and + /// then later loaded when generating the DIVERGE_BLOCK. + llpersonalityslot: Option, + + /// A `Block` for each MIR `BasicBlock` + blocks: Vec>, + + /// An LLVM alloca for each MIR `VarDecl` + vars: Vec>, + + /// An LLVM alloca for each MIR `TempDecl` + temps: Vec>, + + /// The arguments to the function; as args are lvalues, these are + /// always indirect, though we try to avoid creating an alloca + /// when we can (and just reuse the pointer the caller provided). + args: Vec>, +} + +/////////////////////////////////////////////////////////////////////////// + +pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { + let fcx = bcx.fcx; + let mir = bcx.mir(); + + let mir_blocks = bcx.mir().all_basic_blocks(); + + // Allocate variable and temp allocas + let vars = mir.var_decls.iter() + .map(|decl| (bcx.monomorphize(&decl.ty), decl.name)) + .map(|(mty, name)| LvalueRef::alloca(bcx, mty, &name.as_str())) + .collect(); + let temps = mir.temp_decls.iter() + .map(|decl| bcx.monomorphize(&decl.ty)) + .enumerate() + .map(|(i, mty)| LvalueRef::alloca(bcx, mty, &format!("temp{:?}", i))) + .collect(); + let args = arg_value_refs(bcx, mir); + + // Allocate a `Block` for every basic block + let block_bcxs: Vec> = + mir_blocks.iter() + .map(|&bb| fcx.new_block(false, &format!("{:?}", bb), None)) + .collect(); + + // Branch to the START block + let start_bcx = block_bcxs[mir::START_BLOCK.index()]; + build::Br(bcx, start_bcx.llbb, DebugLoc::None); + + let mut mircx = MirContext { + mir: mir, + llpersonalityslot: None, + blocks: block_bcxs, + vars: vars, + temps: temps, + args: args, + }; + + // Translate the body of each block + for &bb in &mir_blocks { + if bb != mir::DIVERGE_BLOCK { + mircx.trans_block(bb); + } + } + + // Total hack: translate DIVERGE_BLOCK last. This is so that any + // panics which the fn may do can initialize the + // `llpersonalityslot` cell. We don't do this up front because the + // LLVM type of it is (frankly) annoying to compute. + mircx.trans_block(mir::DIVERGE_BLOCK); +} + +/// Produce, for each argument, a `ValueRef` pointing at the +/// argument's value. As arguments are lvalues, these are always +/// indirect. +fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>, + mir: &mir::Mir<'tcx>) + -> Vec> { + // FIXME tupled_args? I think I'd rather that mapping is done in MIR land though + let fcx = bcx.fcx; + let tcx = bcx.tcx(); + let mut idx = fcx.arg_offset() as c_uint; + mir.arg_decls + .iter() + .enumerate() + .map(|(arg_index, arg_decl)| { + let arg_ty = bcx.monomorphize(&arg_decl.ty); + let llval = if type_of::arg_is_indirect(bcx.ccx(), arg_ty) { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up, unless + // we emit extra-debug-info, which requires local allocas :(. + // FIXME: lifetimes, debug info + let llarg = llvm::get_param(fcx.llfn, idx); + idx += 1; + llarg + } else if common::type_is_fat_ptr(tcx, arg_ty) { + // we pass fat pointers as two words, but we want to + // represent them internally as a pointer two two words, + // so make an alloca to store them in. + let lldata = llvm::get_param(fcx.llfn, idx); + let llextra = llvm::get_param(fcx.llfn, idx + 1); + idx += 2; + let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); + build::Store(bcx, lldata, expr::get_dataptr(bcx, lltemp)); + build::Store(bcx, llextra, expr::get_dataptr(bcx, lltemp)); + lltemp + } else { + // otherwise, arg is passed by value, so make a + // temporary and store it there + let llarg = llvm::get_param(fcx.llfn, idx); + idx += 1; + let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); + build::Store(bcx, llarg, lltemp); + lltemp + }; + LvalueRef::new(llval, LvalueTy::from_ty(arg_ty)) + }) + .collect() +} + +mod block; +mod constant; +mod lvalue; +mod rvalue; +mod operand; +mod statement; + diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs new file mode 100644 index 000000000000..786b84ae8075 --- /dev/null +++ b/src/librustc_trans/trans/mir/operand.rs @@ -0,0 +1,90 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_mir::repr as mir; +use trans::base; +use trans::build; +use trans::common::Block; +use trans::datum; + +use super::MirContext; + +pub struct OperandRef<'tcx> { + // This will be "indirect" if `appropriate_rvalue_mode` returns + // ByRef, and otherwise ByValue. + pub llval: ValueRef, + + // The type of value being returned. + pub ty: Ty<'tcx> +} + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_operand(&mut self, + bcx: Block<'bcx, 'tcx>, + operand: &mir::Operand<'tcx>) + -> OperandRef<'tcx> + { + debug!("trans_operand(operand={:?})", operand); + + match *operand { + mir::Operand::Consume(ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + debug!("trans_operand: tr_lvalue={} @ {:?}", + bcx.val_to_string(tr_lvalue.llval), + ty); + let llval = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) { + datum::ByValue => build::Load(bcx, tr_lvalue.llval), + datum::ByRef => tr_lvalue.llval, + }; + OperandRef { + llval: llval, + ty: ty + } + } + + mir::Operand::Constant(ref constant) => { + let llval = self.trans_constant(bcx, constant); + let ty = bcx.monomorphize(&constant.ty); + OperandRef { + llval: llval, + ty: ty, + } + } + } + } + + pub fn trans_operand_into(&mut self, + bcx: Block<'bcx, 'tcx>, + lldest: ValueRef, + operand: &mir::Operand<'tcx>) + { + debug!("trans_operand_into(lldest={}, operand={:?})", + bcx.val_to_string(lldest), + operand); + + match *operand { + mir::Operand::Consume(ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let lvalue_ty = tr_lvalue.ty.to_ty(bcx.tcx()); + debug!("trans_operand_into: tr_lvalue={} @ {:?}", + bcx.val_to_string(tr_lvalue.llval), + lvalue_ty); + base::memcpy_ty(bcx, lldest, tr_lvalue.llval, lvalue_ty); + } + + mir::Operand::Constant(..) => { + unimplemented!() + } + } + } +} diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs new file mode 100644 index 000000000000..416aa061276f --- /dev/null +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -0,0 +1,238 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::abi; +use llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_front::hir; +use rustc_mir::repr as mir; + +use trans::asm; +use trans::base; +use trans::build; +use trans::common::{self, Block, Result}; +use trans::debuginfo::DebugLoc; +use trans::declare; +use trans::machine; +use trans::type_::Type; +use trans::type_of; +use trans::tvec; + +use super::MirContext; +use super::operand::OperandRef; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_rvalue(&mut self, + bcx: Block<'bcx, 'tcx>, + lldest: ValueRef, + rvalue: &mir::Rvalue<'tcx>) + -> Block<'bcx, 'tcx> + { + debug!("trans_rvalue(lldest={}, rvalue={:?})", + bcx.val_to_string(lldest), + rvalue); + + match *rvalue { + mir::Rvalue::Use(ref operand) => { + self.trans_operand_into(bcx, lldest, operand); + bcx + } + + mir::Rvalue::Cast(..) => { + unimplemented!() + } + + mir::Rvalue::Repeat(..) => { + unimplemented!() + } + + mir::Rvalue::Ref(_, _, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + // Note: lvalues are indirect, so storing the `llval` into the + // destination effectively creates a reference. + build::Store(bcx, tr_lvalue.llval, lldest); + bcx + } + + mir::Rvalue::Len(ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let (_, lllen) = tvec::get_base_and_len(bcx, + tr_lvalue.llval, + tr_lvalue.ty.to_ty(bcx.tcx())); + build::Store(bcx, lllen, lldest); + bcx + } + + mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { + let lhs = self.trans_operand(bcx, lhs); + let rhs = self.trans_operand(bcx, rhs); + let is_float = lhs.ty.is_fp(); + let is_signed = lhs.ty.is_signed(); + let binop_debug_loc = DebugLoc::None; + let llval = match op { + mir::BinOp::Add => if is_float { + build::FAdd(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Add(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Sub => if is_float { + build::FSub(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Sub(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Mul => if is_float { + build::FMul(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Mul(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Div => if is_float { + build::FDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else if is_signed { + build::SDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::UDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Rem => if is_float { + // LLVM currently always lowers the `frem` instructions appropriate + // library calls typically found in libm. Notably f64 gets wired up + // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for + // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's + // instead just an inline function in a header that goes up to a + // f64, uses `fmod`, and then comes back down to a f32. + // + // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will + // still unconditionally lower frem instructions over 32-bit floats + // to a call to `fmodf`. To work around this we special case MSVC + // 32-bit float rem instructions and instead do the call out to + // `fmod` ourselves. + // + // Note that this is currently duplicated with src/libcore/ops.rs + // which does the same thing, and it would be nice to perhaps unify + // these two implementations on day! Also note that we call `fmod` + // for both 32 and 64-bit floats because if we emit any FRem + // instruction at all then LLVM is capable of optimizing it into a + // 32-bit FRem (which we're trying to avoid). + let tcx = bcx.tcx(); + let use_fmod = tcx.sess.target.target.options.is_like_msvc && + tcx.sess.target.target.arch == "x86"; + if use_fmod { + let f64t = Type::f64(bcx.ccx()); + let fty = Type::func(&[f64t, f64t], &f64t); + let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty, + tcx.types.f64); + if lhs.ty == tcx.types.f32 { + let lllhs = build::FPExt(bcx, lhs.llval, f64t); + let llrhs = build::FPExt(bcx, rhs.llval, f64t); + let llres = build::Call(bcx, llfn, &[lllhs, llrhs], + None, binop_debug_loc); + build::FPTrunc(bcx, llres, Type::f32(bcx.ccx())) + } else { + build::Call(bcx, llfn, &[lhs.llval, rhs.llval], + None, binop_debug_loc) + } + } else { + build::FRem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } + } else if is_signed { + build::SRem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::URem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::BitOr => build::Or(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::BitAnd => build::And(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::BitXor => build::Xor(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::Shl => common::build_unchecked_lshift(bcx, + lhs.llval, + rhs.llval, + binop_debug_loc), + mir::BinOp::Shr => common::build_unchecked_rshift(bcx, + lhs.ty, + lhs.llval, + rhs.llval, + binop_debug_loc), + mir::BinOp::Eq => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiEq, binop_debug_loc), + mir::BinOp::Lt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiLt, binop_debug_loc), + mir::BinOp::Le => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiLe, binop_debug_loc), + mir::BinOp::Ne => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiNe, binop_debug_loc), + mir::BinOp::Ge => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiGe, binop_debug_loc), + mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiGt, binop_debug_loc), + }; + build::Store(bcx, llval, lldest); + bcx + } + + mir::Rvalue::UnaryOp(op, ref operand) => { + let operand = self.trans_operand(bcx, operand); + let is_float = operand.ty.is_fp(); + let debug_loc = DebugLoc::None; + let llval = match op { + mir::UnOp::Not => build::Not(bcx, operand.llval, debug_loc), + mir::UnOp::Neg => if is_float { + build::FNeg(bcx, operand.llval, debug_loc) + } else { + build::Neg(bcx, operand.llval, debug_loc) + } + }; + build::Store(bcx, llval, lldest); + bcx + } + + mir::Rvalue::Box(content_ty) => { + let content_ty: Ty<'tcx> = content_ty; + let llty = type_of::type_of(bcx.ccx(), content_ty); + let llsize = machine::llsize_of(bcx.ccx(), llty); + let align = type_of::align_of(bcx.ccx(), content_ty); + let llalign = common::C_uint(bcx.ccx(), align); + let llty_ptr = llty.ptr_to(); + let box_ty = bcx.tcx().mk_box(content_ty); + let Result { bcx, val: llval } = base::malloc_raw_dyn(bcx, + llty_ptr, + box_ty, + llsize, + llalign, + DebugLoc::None); + build::Store(bcx, llval, lldest); + bcx + } + + mir::Rvalue::Aggregate(_, ref operands) => { + for (i, operand) in operands.iter().enumerate() { + let lldest_i = build::GEPi(bcx, lldest, &[0, i]); + self.trans_operand_into(bcx, lldest_i, operand); + } + bcx + } + + mir::Rvalue::Slice { ref input, from_start, from_end } => { + let ccx = bcx.ccx(); + let input = self.trans_lvalue(bcx, input); + let (llbase, lllen) = tvec::get_base_and_len(bcx, + input.llval, + input.ty.to_ty(bcx.tcx())); + let llbase1 = build::GEPi(bcx, llbase, &[from_start]); + let adj = common::C_uint(ccx, from_start + from_end); + let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None); + build::Store(bcx, llbase1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_ADDR])); + build::Store(bcx, lllen1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_EXTRA])); + bcx + } + + mir::Rvalue::InlineAsm(inline_asm) => { + asm::trans_inline_asm(bcx, inline_asm) + } + } + } +} diff --git a/src/librustc_trans/trans/mir/statement.rs b/src/librustc_trans/trans/mir/statement.rs new file mode 100644 index 000000000000..17a20fb817c8 --- /dev/null +++ b/src/librustc_trans/trans/mir/statement.rs @@ -0,0 +1,48 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::middle::ty::LvaluePreference; +use rustc_mir::repr as mir; +use trans::common::Block; +use trans::debuginfo::DebugLoc; +use trans::glue; + +use super::MirContext; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_statement(&mut self, + bcx: Block<'bcx, 'tcx>, + statement: &mir::Statement<'tcx>) + -> Block<'bcx, 'tcx> { + debug!("trans_statement(statement={:?})", statement); + + match statement.kind { + mir::StatementKind::Assign(ref lvalue, ref rvalue) => { + let tr_dest = self.trans_lvalue(bcx, lvalue); + self.trans_rvalue(bcx, tr_dest.llval, rvalue); + bcx + } + + mir::StatementKind::Drop(mir::DropKind::Deep, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + glue::drop_ty(bcx, tr_lvalue.llval, ty, DebugLoc::None) + } + + mir::StatementKind::Drop(mir::DropKind::Free, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + let content_ty = ty.builtin_deref(true, LvaluePreference::NoPreference); + let content_ty = content_ty.unwrap().ty; + glue::trans_exchange_free_ty(bcx, tr_lvalue.llval, content_ty, DebugLoc::None) + } + } + } +} diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index 04854501312b..fa37b0055398 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -52,6 +52,7 @@ mod llrepr; mod machine; mod _match; mod meth; +mod mir; mod monomorphize; mod tvec; mod type_; diff --git a/src/test/run-pass/mir_trans_spike1.rs b/src/test/run-pass/mir_trans_spike1.rs new file mode 100644 index 000000000000..9a06ab78e73b --- /dev/null +++ b/src/test/run-pass/mir_trans_spike1.rs @@ -0,0 +1,24 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// A simple spike test for MIR version of trans. + +#![feature(rustc_attrs)] + +#[rustc_mir] +fn sum(x: i32, y: i32) -> i32 { + x + y +} + +fn main() { + let x = sum(22, 44); + assert_eq!(x, 66); + println!("sum()={:?}", x); +}