diff options
| author | bors <bors@rust-lang.org> | 2017-11-24 15:11:11 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-11-24 15:11:11 +0000 |
| commit | 5f44c653cff61d0f55f53e07a188f755c7acddd1 (patch) | |
| tree | 5fa1e64973c8f8316b714ceb40ffd31fe5e9539c /src/librustc_mir/transform | |
| parent | 71da1c21ebc79f19e749344c8b4e2c13f533872e (diff) | |
| parent | 42208c122757fca706bc5224f3a0c7200fae32e9 (diff) | |
| download | rust-5f44c653cff61d0f55f53e07a188f755c7acddd1.tar.gz rust-5f44c653cff61d0f55f53e07a188f755c7acddd1.zip | |
Auto merge of #46093 - scottmcm:lower-128-mir, r=nagisa
Add a MIR pass to lower 128-bit operators to lang item calls Runs only with `-Z lower_128bit_ops` since it's not hooked into targets yet. This isn't really useful on its own, but the declarations for the lang items need to be in the compiler before compiler-builtins can be updated to define them, so this is part 1 of at least 3. cc https://github.com/rust-lang/rust/issues/45676 @est31 @nagisa
Diffstat (limited to 'src/librustc_mir/transform')
| -rw-r--r-- | src/librustc_mir/transform/lower_128bit.rs | 240 | ||||
| -rw-r--r-- | src/librustc_mir/transform/mod.rs | 3 |
2 files changed, 243 insertions, 0 deletions
diff --git a/src/librustc_mir/transform/lower_128bit.rs b/src/librustc_mir/transform/lower_128bit.rs new file mode 100644 index 00000000000..9dc5fdadbb1 --- /dev/null +++ b/src/librustc_mir/transform/lower_128bit.rs @@ -0,0 +1,240 @@ +// Copyright 2017 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. + +//! Replaces 128-bit operators with lang item calls + +use rustc::hir::def_id::DefId; +use rustc::middle::lang_items::LangItem; +use rustc::mir::*; +use rustc::ty::{Slice, Ty, TyCtxt, TypeVariants}; +use rustc_data_structures::indexed_vec::{Idx}; +use transform::{MirPass, MirSource}; +use syntax; + +pub struct Lower128Bit; + +impl MirPass for Lower128Bit { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _src: MirSource, + mir: &mut Mir<'tcx>) { + if !tcx.sess.opts.debugging_opts.lower_128bit_ops { + return + } + + self.lower_128bit_ops(tcx, mir); + } +} + +impl Lower128Bit { + fn lower_128bit_ops<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &mut Mir<'tcx>) { + let mut new_blocks = Vec::new(); + let cur_len = mir.basic_blocks().len(); + + let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut(); + for block in basic_blocks.iter_mut() { + for i in (0..block.statements.len()).rev() { + let (lang_item, rhs_kind) = + if let Some((lang_item, rhs_kind)) = + lower_to(&block.statements[i], local_decls, tcx) + { + (lang_item, rhs_kind) + } else { + continue; + }; + + let rhs_override_ty = rhs_kind.ty(tcx); + let cast_local = + match rhs_override_ty { + None => None, + Some(ty) => { + let local_decl = LocalDecl::new_internal( + ty, block.statements[i].source_info.span); + Some(local_decls.push(local_decl)) + }, + }; + + let storage_dead = cast_local.map(|local| { + Statement { + source_info: block.statements[i].source_info, + kind: StatementKind::StorageDead(local), + } + }); + let after_call = BasicBlockData { + statements: storage_dead.into_iter() + .chain(block.statements.drain((i+1)..)).collect(), + is_cleanup: block.is_cleanup, + terminator: block.terminator.take(), + }; + + let bin_statement = block.statements.pop().unwrap(); + let (source_info, lvalue, lhs, mut rhs) = match bin_statement { + Statement { + source_info, + kind: StatementKind::Assign( + lvalue, + Rvalue::BinaryOp(_, lhs, rhs)) + } => (source_info, lvalue, lhs, rhs), + Statement { + source_info, + kind: StatementKind::Assign( + lvalue, + Rvalue::CheckedBinaryOp(_, lhs, rhs)) + } => (source_info, lvalue, lhs, rhs), + _ => bug!("Statement doesn't match pattern any more?"), + }; + + if let Some(local) = cast_local { + block.statements.push(Statement { + source_info: source_info, + kind: StatementKind::StorageLive(local), + }); + block.statements.push(Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(local), + Rvalue::Cast( + CastKind::Misc, + rhs, + rhs_override_ty.unwrap())), + }); + rhs = Operand::Consume(Lvalue::Local(local)); + } + + let call_did = check_lang_item_type( + lang_item, &lvalue, &lhs, &rhs, local_decls, tcx); + + let bb = BasicBlock::new(cur_len + new_blocks.len()); + new_blocks.push(after_call); + + block.terminator = + Some(Terminator { + source_info, + kind: TerminatorKind::Call { + func: Operand::function_handle(tcx, call_did, + Slice::empty(), source_info.span), + args: vec![lhs, rhs], + destination: Some((lvalue, bb)), + cleanup: None, + }, + }); + } + } + + basic_blocks.extend(new_blocks); + } +} + +fn check_lang_item_type<'a, 'tcx, D>( + lang_item: LangItem, + lvalue: &Lvalue<'tcx>, + lhs: &Operand<'tcx>, + rhs: &Operand<'tcx>, + local_decls: &D, + tcx: TyCtxt<'a, 'tcx, 'tcx>) +-> DefId + where D: HasLocalDecls<'tcx> +{ + let did = tcx.require_lang_item(lang_item); + let poly_sig = tcx.fn_sig(did); + let sig = tcx.no_late_bound_regions(&poly_sig).unwrap(); + let lhs_ty = lhs.ty(local_decls, tcx); + let rhs_ty = rhs.ty(local_decls, tcx); + let lvalue_ty = lvalue.ty(local_decls, tcx).to_ty(tcx); + let expected = [lhs_ty, rhs_ty, lvalue_ty]; + assert_eq!(sig.inputs_and_output[..], expected, + "lang item {}", tcx.def_symbol_name(did)); + did +} + +fn lower_to<'a, 'tcx, D>(statement: &Statement<'tcx>, local_decls: &D, tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> Option<(LangItem, RhsKind)> + where D: HasLocalDecls<'tcx> +{ + match statement.kind { + StatementKind::Assign(_, Rvalue::BinaryOp(bin_op, ref lhs, _)) => { + let ty = lhs.ty(local_decls, tcx); + if let Some(is_signed) = sign_of_128bit(ty) { + return item_for_op(bin_op, is_signed); + } + }, + StatementKind::Assign(_, Rvalue::CheckedBinaryOp(bin_op, ref lhs, _)) => { + let ty = lhs.ty(local_decls, tcx); + if let Some(is_signed) = sign_of_128bit(ty) { + return item_for_checked_op(bin_op, is_signed); + } + }, + _ => {}, + } + None +} + +#[derive(Copy, Clone)] +enum RhsKind { + Unchanged, + ForceU128, + ForceU32, +} + +impl RhsKind { + fn ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option<Ty<'tcx>> { + match *self { + RhsKind::Unchanged => None, + RhsKind::ForceU128 => Some(tcx.types.u128), + RhsKind::ForceU32 => Some(tcx.types.u32), + } + } +} + +fn sign_of_128bit(ty: Ty) -> Option<bool> { + match ty.sty { + TypeVariants::TyInt(syntax::ast::IntTy::I128) => Some(true), + TypeVariants::TyUint(syntax::ast::UintTy::U128) => Some(false), + _ => None, + } +} + +fn item_for_op(bin_op: BinOp, is_signed: bool) -> Option<(LangItem, RhsKind)> { + let i = match (bin_op, is_signed) { + (BinOp::Add, true) => (LangItem::I128AddFnLangItem, RhsKind::Unchanged), + (BinOp::Add, false) => (LangItem::U128AddFnLangItem, RhsKind::Unchanged), + (BinOp::Sub, true) => (LangItem::I128SubFnLangItem, RhsKind::Unchanged), + (BinOp::Sub, false) => (LangItem::U128SubFnLangItem, RhsKind::Unchanged), + (BinOp::Mul, true) => (LangItem::I128MulFnLangItem, RhsKind::Unchanged), + (BinOp::Mul, false) => (LangItem::U128MulFnLangItem, RhsKind::Unchanged), + (BinOp::Div, true) => (LangItem::I128DivFnLangItem, RhsKind::Unchanged), + (BinOp::Div, false) => (LangItem::U128DivFnLangItem, RhsKind::Unchanged), + (BinOp::Rem, true) => (LangItem::I128RemFnLangItem, RhsKind::Unchanged), + (BinOp::Rem, false) => (LangItem::U128RemFnLangItem, RhsKind::Unchanged), + (BinOp::Shl, true) => (LangItem::I128ShlFnLangItem, RhsKind::ForceU32), + (BinOp::Shl, false) => (LangItem::U128ShlFnLangItem, RhsKind::ForceU32), + (BinOp::Shr, true) => (LangItem::I128ShrFnLangItem, RhsKind::ForceU32), + (BinOp::Shr, false) => (LangItem::U128ShrFnLangItem, RhsKind::ForceU32), + _ => return None, + }; + Some(i) +} + +fn item_for_checked_op(bin_op: BinOp, is_signed: bool) -> Option<(LangItem, RhsKind)> { + let i = match (bin_op, is_signed) { + (BinOp::Add, true) => (LangItem::I128AddoFnLangItem, RhsKind::Unchanged), + (BinOp::Add, false) => (LangItem::U128AddoFnLangItem, RhsKind::Unchanged), + (BinOp::Sub, true) => (LangItem::I128SuboFnLangItem, RhsKind::Unchanged), + (BinOp::Sub, false) => (LangItem::U128SuboFnLangItem, RhsKind::Unchanged), + (BinOp::Mul, true) => (LangItem::I128MuloFnLangItem, RhsKind::Unchanged), + (BinOp::Mul, false) => (LangItem::U128MuloFnLangItem, RhsKind::Unchanged), + (BinOp::Shl, true) => (LangItem::I128ShloFnLangItem, RhsKind::ForceU128), + (BinOp::Shl, false) => (LangItem::U128ShloFnLangItem, RhsKind::ForceU128), + (BinOp::Shr, true) => (LangItem::I128ShroFnLangItem, RhsKind::ForceU128), + (BinOp::Shr, false) => (LangItem::U128ShroFnLangItem, RhsKind::ForceU128), + _ => bug!("That should be all the checked ones?"), + }; + Some(i) +} \ No newline at end of file diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 441f9be9be1..6987cfa79be 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -42,6 +42,7 @@ pub mod copy_prop; pub mod generator; pub mod inline; pub mod nll; +pub mod lower_128bit; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); @@ -241,6 +242,8 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // From here on out, regions are gone. erase_regions::EraseRegions, + lower_128bit::Lower128Bit, + // Optimizations begin. inline::Inline, instcombine::InstCombine, |
