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 commit is contained in:
Scott McMurray 2017-11-15 23:27:02 -08:00
parent 6160040d85
commit 57c0801e33
7 changed files with 399 additions and 0 deletions

View file

@ -310,6 +310,29 @@ language_item_table! {
NonZeroItem, "non_zero", non_zero;
DebugTraitLangItem, "debug_trait", debug_trait;
// A lang item for each of the 128-bit operators we can optionally lower.
I128AddFnLangItem, "i128_add", i128_add_fn;
I128SubFnLangItem, "i128_sub", i128_sub_fn;
I128MulFnLangItem, "i128_mul", i128_mul_fn;
I128DivFnLangItem, "i128_div", i128_div_fn;
U128DivFnLangItem, "u128_div", u128_div_fn;
I128RemFnLangItem, "i128_rem", i128_rem_fn;
U128RemFnLangItem, "u128_rem", u128_rem_fn;
I128ShlFnLangItem, "i128_shl", i128_shl_fn;
I128ShrFnLangItem, "i128_shr", i128_shr_fn;
U128ShrFnLangItem, "u128_shr", u128_shr_fn;
// And overflow versions for the operators that are checkable.
// While MIR calls these Checked*, they return (T,bool), not Option<T>.
I128AddoFnLangItem, "i128_addo", i128_addo_fn;
U128AddoFnLangItem, "u128_addo", u128_addo_fn;
I128SuboFnLangItem, "i128_subo", i128_subo_fn;
U128SuboFnLangItem, "u128_subo", u128_subo_fn;
I128MuloFnLangItem, "i128_mulo", i128_mulo_fn;
U128MuloFnLangItem, "u128_mulo", u128_mulo_fn;
I128ShloFnLangItem, "i128_shlo", i128_shlo_fn;
I128ShroFnLangItem, "i128_shro", i128_shro_fn;
U128ShroFnLangItem, "u128_shro", u128_shro_fn;
}
impl<'a, 'tcx, 'gcx> TyCtxt<'a, 'tcx, 'gcx> {

View file

@ -170,6 +170,15 @@ impl<'tcx> Mir<'tcx> {
&mut self.basic_blocks
}
#[inline]
pub fn basic_blocks_and_local_decls_mut(&mut self) -> (
&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
&mut LocalDecls<'tcx>,
) {
self.cache.invalidate();
(&mut self.basic_blocks, &mut self.local_decls)
}
#[inline]
pub fn predecessors(&self) -> Ref<IndexVec<BasicBlock, Vec<BasicBlock>>> {
self.cache.predecessors(self)

View file

@ -1142,6 +1142,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
saturating_float_casts: bool = (false, parse_bool, [TRACKED],
"make float->int casts UB-free: numbers outside the integer type's range are clipped to \
the max/min integer respectively, and NaN is mapped to 0"),
lower_128bit_ops: bool = (false, parse_bool, [TRACKED],
"rewrite operators on i128 and u128 into lang item calls (typically provided \
by compiler-builtins) so translation doesn't need to support them"),
}
pub fn default_lib_output() -> CrateType {

View file

@ -0,0 +1,160 @@
// 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 call_did =
if let Some(call_did) = lower_to(&block.statements[i], local_decls, tcx) {
call_did
} else {
continue;
};
let after_call = BasicBlockData {
statements: 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, 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?"),
};
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 lower_to<'a, 'tcx, D>(statement: &Statement<'tcx>, local_decls: &D, tcx: TyCtxt<'a, 'tcx, 'tcx>)
-> Option<DefId>
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) {
if let Some(item) = item_for_op(bin_op, is_signed) {
return Some(tcx.require_lang_item(item))
}
}
},
StatementKind::Assign(_, Rvalue::CheckedBinaryOp(bin_op, ref lhs, _)) => {
let ty = lhs.ty(local_decls, tcx);
if let Some(is_signed) = sign_of_128bit(&ty) {
if let Some(item) = item_for_checked_op(bin_op, is_signed) {
return Some(tcx.require_lang_item(item))
}
}
},
_ => {},
}
None
}
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> {
let i = match (bin_op, is_signed) {
(BinOp::Add, _) => LangItem::I128AddFnLangItem,
(BinOp::Sub, _) => LangItem::I128SubFnLangItem,
(BinOp::Mul, _) => LangItem::I128MulFnLangItem,
(BinOp::Div, true) => LangItem::I128DivFnLangItem,
(BinOp::Div, false) => LangItem::U128DivFnLangItem,
(BinOp::Rem, true) => LangItem::I128RemFnLangItem,
(BinOp::Rem, false) => LangItem::U128RemFnLangItem,
(BinOp::Shl, _) => LangItem::I128ShlFnLangItem,
(BinOp::Shr, true) => LangItem::I128ShrFnLangItem,
(BinOp::Shr, false) => LangItem::U128ShrFnLangItem,
_ => return None,
};
Some(i)
}
fn item_for_checked_op(bin_op: BinOp, is_signed: bool) -> Option<LangItem> {
let i = match (bin_op, is_signed) {
(BinOp::Add, true) => LangItem::I128AddoFnLangItem,
(BinOp::Add, false) => LangItem::U128AddoFnLangItem,
(BinOp::Sub, true) => LangItem::I128SuboFnLangItem,
(BinOp::Sub, false) => LangItem::U128SuboFnLangItem,
(BinOp::Mul, true) => LangItem::I128MuloFnLangItem,
(BinOp::Mul, false) => LangItem::U128MuloFnLangItem,
(BinOp::Shl, _) => LangItem::I128ShloFnLangItem,
(BinOp::Shr, true) => LangItem::I128ShroFnLangItem,
(BinOp::Shr, false) => LangItem::U128ShroFnLangItem,
_ => return None,
};
Some(i)
}

View file

@ -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,

View file

@ -0,0 +1,104 @@
// 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.
// compile-flags: -Z lower_128bit_ops -C debug_assertions=yes
#![feature(i128_type)]
#![feature(lang_items)]
#[lang="i128_div"]
fn i128_div(_x: i128, _y: i128) -> i128 { 3 }
#[lang="u128_div"]
fn u128_div(_x: i128, _y: i128) -> i128 { 4 }
#[lang="i128_rem"]
fn i128_rem(_x: i128, _y: i128) -> i128 { 5 }
#[lang="u128_rem"]
fn u128_rem(_x: i128, _y: i128) -> i128 { 6 }
#[lang="i128_addo"]
fn i128_addo(_x: i128, _y: i128) -> (i128, bool) { (0, false) }
#[lang="u128_addo"]
fn u128_addo(_x: i128, _y: i128) -> (i128, bool) { (1, false) }
#[lang="i128_subo"]
fn i128_subo(_x: i128, _y: i128) -> (i128, bool) { (2, false) }
#[lang="u128_subo"]
fn u128_subo(_x: i128, _y: i128) -> (i128, bool) { (3, false) }
#[lang="i128_mulo"]
fn i128_mulo(_x: i128, _y: i128) -> (i128, bool) { (4, false) }
#[lang="u128_mulo"]
fn u128_mulo(_x: i128, _y: i128) -> (i128, bool) { (5, false) }
#[lang="i128_shlo"]
fn i128_shlo(_x: i128, _y: u32) -> (i128, bool) { (6, false) }
#[lang="i128_shro"]
fn i128_shro(_x: i128, _y: u32) -> (i128, bool) { (7, false) }
#[lang="u128_shro"]
fn u128_shro(_x: i128, _y: u32) -> (i128, bool) { (8, false) }
fn test_signed(mut x: i128) -> i128 {
x += 1;
x -= 2;
x *= 3;
x /= 4;
x %= 5;
x <<= 6;
x >>= 7;
x
}
fn test_unsigned(mut x: u128) -> u128 {
x += 1;
x -= 2;
x *= 3;
x /= 4;
x %= 5;
x <<= 6;
x >>= 7;
x
}
fn main() {
test_signed(-200);
test_unsigned(200);
}
// END RUST SOURCE
// START rustc.test_signed.Lower128Bit.after.mir
// _2 = const i128_addo(_1, const 1i128) -> bb10;
// ...
// _3 = const i128_subo(_1, const 2i128) -> bb11;
// ...
// _4 = const i128_mulo(_1, const 3i128) -> bb12;
// ...
// _1 = const i128_div(_1, const 4i128) -> bb13;
// ...
// _1 = const i128_rem(_1, const 5i128) -> bb15;
// ...
// _14 = const i128_shro(_1, const 7i32) -> bb16;
// ...
// _13 = const i128_shlo(_1, const 6i32) -> bb14;
// END rustc.test_signed.Lower128Bit.after.mir
// START rustc.test_unsigned.Lower128Bit.after.mir
// _2 = const u128_addo(_1, const 1u128) -> bb8;
// ...
// _3 = const u128_subo(_1, const 2u128) -> bb9;
// ...
// _4 = const u128_mulo(_1, const 3u128) -> bb10;
// ...
// _1 = const u128_div(_1, const 4u128) -> bb11;
// ...
// _1 = const u128_rem(_1, const 5u128) -> bb13;
// ...
// _8 = const u128_shro(_1, const 7i32) -> bb14;
// ...
// _7 = const i128_shlo(_1, const 6i32) -> bb12;
// END rustc.test_unsigned.Lower128Bit.after.mir

View file

@ -0,0 +1,97 @@
// 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.
// compile-flags: -Z lower_128bit_ops -C debug_assertions=no
#![feature(i128_type)]
#![feature(lang_items)]
#[lang="i128_add"]
fn i128_add(_x: i128, _y: i128) -> i128 { 0 }
#[lang="i128_sub"]
fn i128_sub(_x: i128, _y: i128) -> i128 { 1 }
#[lang="i128_mul"]
fn i128_mul(_x: i128, _y: i128) -> i128 { 2 }
#[lang="i128_div"]
fn i128_div(_x: i128, _y: i128) -> i128 { 3 }
#[lang="u128_div"]
fn u128_div(_x: i128, _y: i128) -> i128 { 4 }
#[lang="i128_rem"]
fn i128_rem(_x: i128, _y: i128) -> i128 { 5 }
#[lang="u128_rem"]
fn u128_rem(_x: i128, _y: i128) -> i128 { 6 }
#[lang="i128_shl"]
fn i128_shl(_x: i128, _y: u32) -> i128 { 7 }
#[lang="i128_shr"]
fn i128_shr(_x: i128, _y: u32) -> i128 { 8 }
#[lang="u128_shr"]
fn u128_shr(_x: i128, _y: u32) -> i128 { 9 }
fn test_signed(mut x: i128) -> i128 {
x += 1;
x -= 2;
x *= 3;
x /= 4;
x %= 5;
x <<= 6;
x >>= 7;
x
}
fn test_unsigned(mut x: u128) -> u128 {
x += 1;
x -= 2;
x *= 3;
x /= 4;
x %= 5;
x <<= 6;
x >>= 7;
x
}
fn main() {
test_signed(-200);
test_unsigned(200);
}
// END RUST SOURCE
// START rustc.test_signed.Lower128Bit.after.mir
// _1 = const i128_add(_1, const 1i128) -> bb7;
// ...
// _1 = const i128_div(_1, const 4i128) -> bb8;
// ...
// _1 = const i128_rem(_1, const 5i128) -> bb11;
// ...
// _1 = const i128_mul(_1, const 3i128) -> bb5;
// ...
// _1 = const i128_sub(_1, const 2i128) -> bb6;
// ...
// _1 = const i128_shr(_1, const 7i32) -> bb9;
// ...
// _1 = const i128_shl(_1, const 6i32) -> bb10;
// END rustc.test_signed.Lower128Bit.after.mir
// START rustc.test_unsigned.Lower128Bit.after.mir
// _1 = const i128_add(_1, const 1u128) -> bb5;
// ...
// _1 = const u128_div(_1, const 4u128) -> bb6;
// ...
// _1 = const u128_rem(_1, const 5u128) -> bb9;
// ...
// _1 = const i128_mul(_1, const 3u128) -> bb3;
// ...
// _1 = const i128_sub(_1, const 2u128) -> bb4;
// ...
// _1 = const u128_shr(_1, const 7i32) -> bb7;
// ...
// _1 = const i128_shl(_1, const 6i32) -> bb8;
// END rustc.test_unsigned.Lower128Bit.after.mir