Skip to content

Commit

Permalink
Handle shifts properly
Browse files Browse the repository at this point in the history
* The overflow-checking shift items need to take a full 128-bit type, since they need to be able to detect idiocy like `1i128 << (1u128 << 127)`
* The unchecked ones just take u32, like the `*_sh?` methods in core
* Because shift-by-anything is allowed, cast into a new local for every shift
  • Loading branch information
scottmcm committed Nov 20, 2017
1 parent 6a5a086 commit 42208c1
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 48 deletions.
118 changes: 86 additions & 32 deletions src/librustc_mir/transform/lower_128bit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,41 @@ impl Lower128Bit {
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 =
if let Some(lang_item) = lower_to(&block.statements[i], local_decls, tcx) {
lang_item
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: block.statements.drain((i+1)..).collect(),
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, rhs) = match bin_statement {
let (source_info, lvalue, lhs, mut rhs) = match bin_statement {
Statement {
source_info,
kind: StatementKind::Assign(
Expand All @@ -71,6 +91,23 @@ impl Lower128Bit {
_ => 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);

Expand Down Expand Up @@ -118,7 +155,7 @@ fn check_lang_item_type<'a, 'tcx, D>(
}

fn lower_to<'a, 'tcx, D>(statement: &Statement<'tcx>, local_decls: &D, tcx: TyCtxt<'a, 'tcx, 'tcx>)
-> Option<LangItem>
-> Option<(LangItem, RhsKind)>
where D: HasLocalDecls<'tcx>
{
match statement.kind {
Expand All @@ -139,6 +176,23 @@ fn lower_to<'a, 'tcx, D>(statement: &Statement<'tcx>, local_decls: &D, tcx: TyCt
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),
Expand All @@ -147,39 +201,39 @@ fn sign_of_128bit(ty: Ty) -> Option<bool> {
}
}

fn item_for_op(bin_op: BinOp, is_signed: bool) -> Option<LangItem> {
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,
(BinOp::Add, false) => LangItem::U128AddFnLangItem,
(BinOp::Sub, true) => LangItem::I128SubFnLangItem,
(BinOp::Sub, false) => LangItem::U128SubFnLangItem,
(BinOp::Mul, true) => LangItem::I128MulFnLangItem,
(BinOp::Mul, false) => LangItem::U128MulFnLangItem,
(BinOp::Div, true) => LangItem::I128DivFnLangItem,
(BinOp::Div, false) => LangItem::U128DivFnLangItem,
(BinOp::Rem, true) => LangItem::I128RemFnLangItem,
(BinOp::Rem, false) => LangItem::U128RemFnLangItem,
(BinOp::Shl, true) => LangItem::I128ShlFnLangItem,
(BinOp::Shl, false) => LangItem::U128ShlFnLangItem,
(BinOp::Shr, true) => LangItem::I128ShrFnLangItem,
(BinOp::Shr, false) => LangItem::U128ShrFnLangItem,
(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> {
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,
(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, true) => LangItem::I128ShloFnLangItem,
(BinOp::Shl, false) => LangItem::U128ShloFnLangItem,
(BinOp::Shr, true) => LangItem::I128ShroFnLangItem,
(BinOp::Shr, false) => LangItem::U128ShroFnLangItem,
(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)
Expand Down
22 changes: 14 additions & 8 deletions src/test/mir-opt/lower_128bit_debug_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ fn i128_mulo(_x: i128, _y: i128) -> (i128, bool) { (4, false) }
#[lang="u128_mulo"]
fn u128_mulo(_x: u128, _y: u128) -> (u128, bool) { (5, false) }
#[lang="i128_shlo"]
fn i128_shlo(_x: i128, _y: i32) -> (i128, bool) { (6, false) }
fn i128_shlo(_x: i128, _y: u128) -> (i128, bool) { (6, false) }
#[lang="u128_shlo"]
fn u128_shlo(_x: u128, _y: i32) -> (u128, bool) { (6, false) }
fn u128_shlo(_x: u128, _y: u128) -> (u128, bool) { (6, false) }
#[lang="i128_shro"]
fn i128_shro(_x: i128, _y: i32) -> (i128, bool) { (7, false) }
fn i128_shro(_x: i128, _y: u128) -> (i128, bool) { (7, false) }
#[lang="u128_shro"]
fn u128_shro(_x: u128, _y: i32) -> (u128, bool) { (8, false) }
fn u128_shro(_x: u128, _y: u128) -> (u128, bool) { (8, false) }

fn test_signed(mut x: i128) -> i128 {
x += 1;
Expand Down Expand Up @@ -88,7 +88,9 @@ fn main() {
// _1 = const i128_rem(_1, const 5i128) -> bb15;
// ...
// _1 = (_13.0: i128);
// _14 = const i128_shro(_1, const 7i32) -> bb16;
// ...
// _17 = const 7i32 as u128 (Misc);
// _14 = const i128_shro(_1, _17) -> bb16;
// ...
// _1 = (_14.0: i128);
// ...
Expand All @@ -100,7 +102,8 @@ fn main() {
// ...
// assert(!(_13.1: bool), "attempt to shift left with overflow") -> bb8;
// ...
// _13 = const i128_shlo(_1, const 6i32) -> bb14;
// _16 = const 6i32 as u128 (Misc);
// _13 = const i128_shlo(_1, _16) -> bb14;
// ...
// assert(!(_14.1: bool), "attempt to shift right with overflow") -> bb9;
// END rustc.test_signed.Lower128Bit.after.mir
Expand All @@ -121,7 +124,9 @@ fn main() {
// _1 = const u128_rem(_1, const 5u128) -> bb13;
// ...
// _1 = (_7.0: u128);
// _8 = const u128_shro(_1, const 7i32) -> bb14;
// ...
// _11 = const 7i32 as u128 (Misc);
// _8 = const u128_shro(_1, _11) -> bb14;
// ...
// _1 = (_8.0: u128);
// ...
Expand All @@ -133,7 +138,8 @@ fn main() {
// ...
// assert(!(_7.1: bool), "attempt to shift left with overflow") -> bb6;
// ...
// _7 = const u128_shlo(_1, const 6i32) -> bb12;
// _10 = const 6i32 as u128 (Misc);
// _7 = const u128_shlo(_1, _10) -> bb12;
// ...
// assert(!(_8.1: bool), "attempt to shift right with overflow") -> bb7;
// END rustc.test_unsigned.Lower128Bit.after.mir
20 changes: 12 additions & 8 deletions src/test/mir-opt/lower_128bit_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ fn i128_rem(_x: i128, _y: i128) -> i128 { 5 }
#[lang="u128_rem"]
fn u128_rem(_x: u128, _y: u128) -> u128 { 6 }
#[lang="i128_shl"]
fn i128_shl(_x: i128, _y: i32) -> i128 { 7 }
fn i128_shl(_x: i128, _y: u32) -> i128 { 7 }
#[lang="u128_shl"]
fn u128_shl(_x: u128, _y: i32) -> u128 { 7 }
fn u128_shl(_x: u128, _y: u32) -> u128 { 7 }
#[lang="i128_shr"]
fn i128_shr(_x: i128, _y: i32) -> i128 { 8 }
fn i128_shr(_x: i128, _y: u32) -> i128 { 8 }
#[lang="u128_shr"]
fn u128_shr(_x: u128, _y: i32) -> u128 { 9 }
fn u128_shr(_x: u128, _y: u32) -> u128 { 9 }

fn test_signed(mut x: i128) -> i128 {
x += 1;
Expand Down Expand Up @@ -82,9 +82,11 @@ fn main() {
// ...
// _1 = const i128_sub(_1, const 2i128) -> bb6;
// ...
// _1 = const i128_shr(_1, const 7i32) -> bb9;
// _11 = const 7i32 as u32 (Misc);
// _1 = const i128_shr(_1, _11) -> bb9;
// ...
// _1 = const i128_shl(_1, const 6i32) -> bb10;
// _12 = const 6i32 as u32 (Misc);
// _1 = const i128_shl(_1, _12) -> bb10;
// END rustc.test_signed.Lower128Bit.after.mir

// START rustc.test_unsigned.Lower128Bit.after.mir
Expand All @@ -98,7 +100,9 @@ fn main() {
// ...
// _1 = const u128_sub(_1, const 2u128) -> bb4;
// ...
// _1 = const u128_shr(_1, const 7i32) -> bb7;
// _5 = const 7i32 as u32 (Misc);
// _1 = const u128_shr(_1, _5) -> bb7;
// ...
// _1 = const u128_shl(_1, const 6i32) -> bb8;
// _6 = const 6i32 as u32 (Misc);
// _1 = const u128_shl(_1, _6) -> bb8;
// END rustc.test_unsigned.Lower128Bit.after.mir

0 comments on commit 42208c1

Please sign in to comment.