about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-11-24 15:11:11 +0000
committerbors <bors@rust-lang.org>2017-11-24 15:11:11 +0000
commit5f44c653cff61d0f55f53e07a188f755c7acddd1 (patch)
tree5fa1e64973c8f8316b714ceb40ffd31fe5e9539c /src
parent71da1c21ebc79f19e749344c8b4e2c13f533872e (diff)
parent42208c122757fca706bc5224f3a0c7200fae32e9 (diff)
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')
-rw-r--r--src/librustc/middle/lang_items.rs28
-rw-r--r--src/librustc/mir/mod.rs9
-rw-r--r--src/librustc/session/config.rs3
-rw-r--r--src/librustc_mir/transform/lower_128bit.rs240
-rw-r--r--src/librustc_mir/transform/mod.rs3
-rw-r--r--src/test/mir-opt/lower_128bit_debug_test.rs145
-rw-r--r--src/test/mir-opt/lower_128bit_test.rs108
7 files changed, 536 insertions, 0 deletions
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index 19a43f3b5dd..a858a8d7449 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -310,6 +310,34 @@ 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;
+    U128AddFnLangItem,               "u128_add",                u128_add_fn;
+    I128SubFnLangItem,               "i128_sub",                i128_sub_fn;
+    U128SubFnLangItem,               "u128_sub",                u128_sub_fn;
+    I128MulFnLangItem,               "i128_mul",                i128_mul_fn;
+    U128MulFnLangItem,               "u128_mul",                u128_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;
+    U128ShlFnLangItem,               "u128_shl",                u128_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;
+    U128ShloFnLangItem,              "u128_shlo",               u128_shlo_fn;
+    I128ShroFnLangItem,              "i128_shro",               i128_shro_fn;
+    U128ShroFnLangItem,              "u128_shro",               u128_shro_fn;
 }
 
 impl<'a, 'tcx, 'gcx> TyCtxt<'a, 'tcx, 'gcx> {
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 94e8033ed16..d093ab45b55 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -171,6 +171,15 @@ impl<'tcx> Mir<'tcx> {
     }
 
     #[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)
     }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index ee197a009f8..a56f850942c 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1144,6 +1144,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 {
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,
diff --git a/src/test/mir-opt/lower_128bit_debug_test.rs b/src/test/mir-opt/lower_128bit_debug_test.rs
new file mode 100644
index 00000000000..4626dc17e1f
--- /dev/null
+++ b/src/test/mir-opt/lower_128bit_debug_test.rs
@@ -0,0 +1,145 @@
+// 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: u128, _y: u128) -> u128 { 4 }
+#[lang="i128_rem"]
+fn i128_rem(_x: i128, _y: i128) -> i128 { 5 }
+#[lang="u128_rem"]
+fn u128_rem(_x: u128, _y: u128) -> u128 { 6 }
+
+#[lang="i128_addo"]
+fn i128_addo(_x: i128, _y: i128) -> (i128, bool) { (0, false) }
+#[lang="u128_addo"]
+fn u128_addo(_x: u128, _y: u128) -> (u128, bool) { (1, false) }
+#[lang="i128_subo"]
+fn i128_subo(_x: i128, _y: i128) -> (i128, bool) { (2, false) }
+#[lang="u128_subo"]
+fn u128_subo(_x: u128, _y: u128) -> (u128, bool) { (3, false) }
+#[lang="i128_mulo"]
+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: u128) -> (i128, bool) { (6, false) }
+#[lang="u128_shlo"]
+fn u128_shlo(_x: u128, _y: u128) -> (u128, bool) { (6, false) }
+#[lang="i128_shro"]
+fn i128_shro(_x: i128, _y: u128) -> (i128, bool) { (7, false) }
+#[lang="u128_shro"]
+fn u128_shro(_x: u128, _y: u128) -> (u128, 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;
+//     ...
+//     _1 = (_2.0: i128);
+//     _3 = const i128_subo(_1, const 2i128) -> bb11;
+//     ...
+//     _1 = (_3.0: i128);
+//     _4 = const i128_mulo(_1, const 3i128) -> bb12;
+//     ...
+//     _1 = (_4.0: i128);
+//     ...
+//     _1 = const i128_div(_1, const 4i128) -> bb13;
+//     ...
+//     _1 = const i128_rem(_1, const 5i128) -> bb15;
+//     ...
+//     _1 = (_13.0: i128);
+//     ...
+//     _17 = const 7i32 as u128 (Misc);
+//     _14 = const i128_shro(_1, _17) -> bb16;
+//     ...
+//     _1 = (_14.0: i128);
+//     ...
+//     assert(!(_2.1: bool), "attempt to add with overflow") -> bb1;
+//     ...
+//     assert(!(_3.1: bool), "attempt to subtract with overflow") -> bb2;
+//     ...
+//     assert(!(_4.1: bool), "attempt to multiply with overflow") -> bb3;
+//     ...
+//     assert(!(_13.1: bool), "attempt to shift left with overflow") -> bb8;
+//     ...
+//     _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
+
+// START rustc.test_unsigned.Lower128Bit.after.mir
+//     _2 = const u128_addo(_1, const 1u128) -> bb8;
+//     ...
+//     _1 = (_2.0: u128);
+//     _3 = const u128_subo(_1, const 2u128) -> bb9;
+//     ...
+//     _1 = (_3.0: u128);
+//     _4 = const u128_mulo(_1, const 3u128) -> bb10;
+//     ...
+//     _1 = (_4.0: u128);
+//     ...
+//     _1 = const u128_div(_1, const 4u128) -> bb11;
+//     ...
+//     _1 = const u128_rem(_1, const 5u128) -> bb13;
+//     ...
+//     _1 = (_7.0: u128);
+//     ...
+//     _11 = const 7i32 as u128 (Misc);
+//     _8 = const u128_shro(_1, _11) -> bb14;
+//     ...
+//     _1 = (_8.0: u128);
+//     ...
+//     assert(!(_2.1: bool), "attempt to add with overflow") -> bb1;
+//     ...
+//     assert(!(_3.1: bool), "attempt to subtract with overflow") -> bb2;
+//     ...
+//     assert(!(_4.1: bool), "attempt to multiply with overflow") -> bb3;
+//     ...
+//     assert(!(_7.1: bool), "attempt to shift left with overflow") -> bb6;
+//     ...
+//     _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
diff --git a/src/test/mir-opt/lower_128bit_test.rs b/src/test/mir-opt/lower_128bit_test.rs
new file mode 100644
index 00000000000..207cd0ac57e
--- /dev/null
+++ b/src/test/mir-opt/lower_128bit_test.rs
@@ -0,0 +1,108 @@
+// 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="u128_add"]
+fn u128_add(_x: u128, _y: u128) -> u128 { 0 }
+#[lang="i128_sub"]
+fn i128_sub(_x: i128, _y: i128) -> i128 { 1 }
+#[lang="u128_sub"]
+fn u128_sub(_x: u128, _y: u128) -> u128 { 1 }
+#[lang="i128_mul"]
+fn i128_mul(_x: i128, _y: i128) -> i128 { 2 }
+#[lang="u128_mul"]
+fn u128_mul(_x: u128, _y: u128) -> u128 { 2 }
+#[lang="i128_div"]
+fn i128_div(_x: i128, _y: i128) -> i128 { 3 }
+#[lang="u128_div"]
+fn u128_div(_x: u128, _y: u128) -> u128 { 4 }
+#[lang="i128_rem"]
+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: u32) -> i128 { 7 }
+#[lang="u128_shl"]
+fn u128_shl(_x: u128, _y: u32) -> u128 { 7 }
+#[lang="i128_shr"]
+fn i128_shr(_x: i128, _y: u32) -> i128 { 8 }
+#[lang="u128_shr"]
+fn u128_shr(_x: u128, _y: u32) -> u128 { 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;
+//     ...
+//     _11 = const 7i32 as u32 (Misc);
+//     _1 = const i128_shr(_1, _11) -> bb9;
+//     ...
+//     _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
+//     _1 = const u128_add(_1, const 1u128) -> bb5;
+//     ...
+//     _1 = const u128_div(_1, const 4u128) -> bb6;
+//     ...
+//     _1 = const u128_rem(_1, const 5u128) -> bb9;
+//     ...
+//     _1 = const u128_mul(_1, const 3u128) -> bb3;
+//     ...
+//     _1 = const u128_sub(_1, const 2u128) -> bb4;
+//     ...
+//     _5 = const 7i32 as u32 (Misc);
+//     _1 = const u128_shr(_1, _5) -> bb7;
+//     ...
+//     _6 = const 6i32 as u32 (Misc);
+//     _1 = const u128_shl(_1, _6) -> bb8;
+// END rustc.test_unsigned.Lower128Bit.after.mir