Skip to content

Add a MIR pass to lower 128-bit operators to lang item calls #46093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 24, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
9 changes: 9 additions & 0 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
160 changes: 160 additions & 0 deletions src/librustc_mir/transform/lower_128bit.rs
Original file line number Diff line number Diff line change
@@ -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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since lang-items aren’t yet type-checked, could you add an assertion of some sort that checks whether the function really has the expected type?

#9307 is a long standing issue regarding that, and I would rather have an assertion in an easy to track location rather than right before trans.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, the next update generates an error like

thread 'rustc' panicked at 'assertion failed: `(left == right)`
  left: `[i128, i128, (i128, u32)]`,
 right: `[i128, i128, (i128, bool)]`: lang item _ZN23lower_128bit_debug_test9i128_addo', src\librustc_mir\transform\lower_128bit.rs:115:4

}
}
},
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)
}
3 changes: 3 additions & 0 deletions src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
104 changes: 104 additions & 0 deletions src/test/mir-opt/lower_128bit_debug_test.rs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should include the statements dealing with the tuple projections (i.e. the branch on _2.1 and the _1 = _2.0).

// ...
// _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
Loading