Skip to content

Commit 57c0801

Browse files
committed
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.
1 parent 6160040 commit 57c0801

File tree

7 files changed

+399
-0
lines changed

7 files changed

+399
-0
lines changed

src/librustc/middle/lang_items.rs

+23
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,29 @@ language_item_table! {
310310
NonZeroItem, "non_zero", non_zero;
311311

312312
DebugTraitLangItem, "debug_trait", debug_trait;
313+
314+
// A lang item for each of the 128-bit operators we can optionally lower.
315+
I128AddFnLangItem, "i128_add", i128_add_fn;
316+
I128SubFnLangItem, "i128_sub", i128_sub_fn;
317+
I128MulFnLangItem, "i128_mul", i128_mul_fn;
318+
I128DivFnLangItem, "i128_div", i128_div_fn;
319+
U128DivFnLangItem, "u128_div", u128_div_fn;
320+
I128RemFnLangItem, "i128_rem", i128_rem_fn;
321+
U128RemFnLangItem, "u128_rem", u128_rem_fn;
322+
I128ShlFnLangItem, "i128_shl", i128_shl_fn;
323+
I128ShrFnLangItem, "i128_shr", i128_shr_fn;
324+
U128ShrFnLangItem, "u128_shr", u128_shr_fn;
325+
// And overflow versions for the operators that are checkable.
326+
// While MIR calls these Checked*, they return (T,bool), not Option<T>.
327+
I128AddoFnLangItem, "i128_addo", i128_addo_fn;
328+
U128AddoFnLangItem, "u128_addo", u128_addo_fn;
329+
I128SuboFnLangItem, "i128_subo", i128_subo_fn;
330+
U128SuboFnLangItem, "u128_subo", u128_subo_fn;
331+
I128MuloFnLangItem, "i128_mulo", i128_mulo_fn;
332+
U128MuloFnLangItem, "u128_mulo", u128_mulo_fn;
333+
I128ShloFnLangItem, "i128_shlo", i128_shlo_fn;
334+
I128ShroFnLangItem, "i128_shro", i128_shro_fn;
335+
U128ShroFnLangItem, "u128_shro", u128_shro_fn;
313336
}
314337

315338
impl<'a, 'tcx, 'gcx> TyCtxt<'a, 'tcx, 'gcx> {

src/librustc/mir/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ impl<'tcx> Mir<'tcx> {
170170
&mut self.basic_blocks
171171
}
172172

173+
#[inline]
174+
pub fn basic_blocks_and_local_decls_mut(&mut self) -> (
175+
&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
176+
&mut LocalDecls<'tcx>,
177+
) {
178+
self.cache.invalidate();
179+
(&mut self.basic_blocks, &mut self.local_decls)
180+
}
181+
173182
#[inline]
174183
pub fn predecessors(&self) -> Ref<IndexVec<BasicBlock, Vec<BasicBlock>>> {
175184
self.cache.predecessors(self)

src/librustc/session/config.rs

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

11471150
pub fn default_lib_output() -> CrateType {
+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Replaces 128-bit operators with lang item calls
12+
13+
use rustc::hir::def_id::DefId;
14+
use rustc::middle::lang_items::LangItem;
15+
use rustc::mir::*;
16+
use rustc::ty::{Slice, Ty, TyCtxt, TypeVariants};
17+
use rustc_data_structures::indexed_vec::{Idx};
18+
use transform::{MirPass, MirSource};
19+
use syntax;
20+
21+
pub struct Lower128Bit;
22+
23+
impl MirPass for Lower128Bit {
24+
fn run_pass<'a, 'tcx>(&self,
25+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
26+
_src: MirSource,
27+
mir: &mut Mir<'tcx>) {
28+
if !tcx.sess.opts.debugging_opts.lower_128bit_ops {
29+
return
30+
}
31+
32+
self.lower_128bit_ops(tcx, mir);
33+
}
34+
}
35+
36+
impl Lower128Bit {
37+
fn lower_128bit_ops<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &mut Mir<'tcx>) {
38+
let mut new_blocks = Vec::new();
39+
let cur_len = mir.basic_blocks().len();
40+
41+
let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut();
42+
for block in basic_blocks.iter_mut() {
43+
for i in (0..block.statements.len()).rev() {
44+
let call_did =
45+
if let Some(call_did) = lower_to(&block.statements[i], local_decls, tcx) {
46+
call_did
47+
} else {
48+
continue;
49+
};
50+
51+
let after_call = BasicBlockData {
52+
statements: block.statements.drain((i+1)..).collect(),
53+
is_cleanup: block.is_cleanup,
54+
terminator: block.terminator.take(),
55+
};
56+
57+
let bin_statement = block.statements.pop().unwrap();
58+
let (source_info, lvalue, lhs, rhs) = match bin_statement {
59+
Statement {
60+
source_info,
61+
kind: StatementKind::Assign(
62+
lvalue,
63+
Rvalue::BinaryOp(_, lhs, rhs))
64+
} => (source_info, lvalue, lhs, rhs),
65+
Statement {
66+
source_info,
67+
kind: StatementKind::Assign(
68+
lvalue,
69+
Rvalue::CheckedBinaryOp(_, lhs, rhs))
70+
} => (source_info, lvalue, lhs, rhs),
71+
_ => bug!("Statement doesn't match pattern any more?"),
72+
};
73+
74+
let bb = BasicBlock::new(cur_len + new_blocks.len());
75+
new_blocks.push(after_call);
76+
77+
block.terminator =
78+
Some(Terminator {
79+
source_info,
80+
kind: TerminatorKind::Call {
81+
func: Operand::function_handle(tcx, call_did,
82+
Slice::empty(), source_info.span),
83+
args: vec![lhs, rhs],
84+
destination: Some((lvalue, bb)),
85+
cleanup: None,
86+
},
87+
});
88+
}
89+
}
90+
91+
basic_blocks.extend(new_blocks);
92+
}
93+
}
94+
95+
fn lower_to<'a, 'tcx, D>(statement: &Statement<'tcx>, local_decls: &D, tcx: TyCtxt<'a, 'tcx, 'tcx>)
96+
-> Option<DefId>
97+
where D: HasLocalDecls<'tcx>
98+
{
99+
match statement.kind {
100+
StatementKind::Assign(_, Rvalue::BinaryOp(bin_op, ref lhs, _)) => {
101+
let ty = lhs.ty(local_decls, tcx);
102+
if let Some(is_signed) = sign_of_128bit(&ty) {
103+
if let Some(item) = item_for_op(bin_op, is_signed) {
104+
return Some(tcx.require_lang_item(item))
105+
}
106+
}
107+
},
108+
StatementKind::Assign(_, Rvalue::CheckedBinaryOp(bin_op, ref lhs, _)) => {
109+
let ty = lhs.ty(local_decls, tcx);
110+
if let Some(is_signed) = sign_of_128bit(&ty) {
111+
if let Some(item) = item_for_checked_op(bin_op, is_signed) {
112+
return Some(tcx.require_lang_item(item))
113+
}
114+
}
115+
},
116+
_ => {},
117+
}
118+
None
119+
}
120+
121+
fn sign_of_128bit(ty: &Ty) -> Option<bool> {
122+
match ty.sty {
123+
TypeVariants::TyInt(syntax::ast::IntTy::I128) => Some(true),
124+
TypeVariants::TyUint(syntax::ast::UintTy::U128) => Some(false),
125+
_ => None,
126+
}
127+
}
128+
129+
fn item_for_op(bin_op: BinOp, is_signed: bool) -> Option<LangItem> {
130+
let i = match (bin_op, is_signed) {
131+
(BinOp::Add, _) => LangItem::I128AddFnLangItem,
132+
(BinOp::Sub, _) => LangItem::I128SubFnLangItem,
133+
(BinOp::Mul, _) => LangItem::I128MulFnLangItem,
134+
(BinOp::Div, true) => LangItem::I128DivFnLangItem,
135+
(BinOp::Div, false) => LangItem::U128DivFnLangItem,
136+
(BinOp::Rem, true) => LangItem::I128RemFnLangItem,
137+
(BinOp::Rem, false) => LangItem::U128RemFnLangItem,
138+
(BinOp::Shl, _) => LangItem::I128ShlFnLangItem,
139+
(BinOp::Shr, true) => LangItem::I128ShrFnLangItem,
140+
(BinOp::Shr, false) => LangItem::U128ShrFnLangItem,
141+
_ => return None,
142+
};
143+
Some(i)
144+
}
145+
146+
fn item_for_checked_op(bin_op: BinOp, is_signed: bool) -> Option<LangItem> {
147+
let i = match (bin_op, is_signed) {
148+
(BinOp::Add, true) => LangItem::I128AddoFnLangItem,
149+
(BinOp::Add, false) => LangItem::U128AddoFnLangItem,
150+
(BinOp::Sub, true) => LangItem::I128SuboFnLangItem,
151+
(BinOp::Sub, false) => LangItem::U128SuboFnLangItem,
152+
(BinOp::Mul, true) => LangItem::I128MuloFnLangItem,
153+
(BinOp::Mul, false) => LangItem::U128MuloFnLangItem,
154+
(BinOp::Shl, _) => LangItem::I128ShloFnLangItem,
155+
(BinOp::Shr, true) => LangItem::I128ShroFnLangItem,
156+
(BinOp::Shr, false) => LangItem::U128ShroFnLangItem,
157+
_ => return None,
158+
};
159+
Some(i)
160+
}

src/librustc_mir/transform/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub mod copy_prop;
4242
pub mod generator;
4343
pub mod inline;
4444
pub mod nll;
45+
pub mod lower_128bit;
4546

4647
pub(crate) fn provide(providers: &mut Providers) {
4748
self::qualify_consts::provide(providers);
@@ -241,6 +242,8 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
241242
// From here on out, regions are gone.
242243
erase_regions::EraseRegions,
243244

245+
lower_128bit::Lower128Bit,
246+
244247
// Optimizations begin.
245248
inline::Inline,
246249
instcombine::InstCombine,
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z lower_128bit_ops -C debug_assertions=yes
12+
13+
#![feature(i128_type)]
14+
#![feature(lang_items)]
15+
16+
#[lang="i128_div"]
17+
fn i128_div(_x: i128, _y: i128) -> i128 { 3 }
18+
#[lang="u128_div"]
19+
fn u128_div(_x: i128, _y: i128) -> i128 { 4 }
20+
#[lang="i128_rem"]
21+
fn i128_rem(_x: i128, _y: i128) -> i128 { 5 }
22+
#[lang="u128_rem"]
23+
fn u128_rem(_x: i128, _y: i128) -> i128 { 6 }
24+
25+
#[lang="i128_addo"]
26+
fn i128_addo(_x: i128, _y: i128) -> (i128, bool) { (0, false) }
27+
#[lang="u128_addo"]
28+
fn u128_addo(_x: i128, _y: i128) -> (i128, bool) { (1, false) }
29+
#[lang="i128_subo"]
30+
fn i128_subo(_x: i128, _y: i128) -> (i128, bool) { (2, false) }
31+
#[lang="u128_subo"]
32+
fn u128_subo(_x: i128, _y: i128) -> (i128, bool) { (3, false) }
33+
#[lang="i128_mulo"]
34+
fn i128_mulo(_x: i128, _y: i128) -> (i128, bool) { (4, false) }
35+
#[lang="u128_mulo"]
36+
fn u128_mulo(_x: i128, _y: i128) -> (i128, bool) { (5, false) }
37+
#[lang="i128_shlo"]
38+
fn i128_shlo(_x: i128, _y: u32) -> (i128, bool) { (6, false) }
39+
#[lang="i128_shro"]
40+
fn i128_shro(_x: i128, _y: u32) -> (i128, bool) { (7, false) }
41+
#[lang="u128_shro"]
42+
fn u128_shro(_x: i128, _y: u32) -> (i128, bool) { (8, false) }
43+
44+
45+
fn test_signed(mut x: i128) -> i128 {
46+
x += 1;
47+
x -= 2;
48+
x *= 3;
49+
x /= 4;
50+
x %= 5;
51+
x <<= 6;
52+
x >>= 7;
53+
x
54+
}
55+
56+
fn test_unsigned(mut x: u128) -> u128 {
57+
x += 1;
58+
x -= 2;
59+
x *= 3;
60+
x /= 4;
61+
x %= 5;
62+
x <<= 6;
63+
x >>= 7;
64+
x
65+
}
66+
67+
fn main() {
68+
test_signed(-200);
69+
test_unsigned(200);
70+
}
71+
72+
// END RUST SOURCE
73+
74+
// START rustc.test_signed.Lower128Bit.after.mir
75+
// _2 = const i128_addo(_1, const 1i128) -> bb10;
76+
// ...
77+
// _3 = const i128_subo(_1, const 2i128) -> bb11;
78+
// ...
79+
// _4 = const i128_mulo(_1, const 3i128) -> bb12;
80+
// ...
81+
// _1 = const i128_div(_1, const 4i128) -> bb13;
82+
// ...
83+
// _1 = const i128_rem(_1, const 5i128) -> bb15;
84+
// ...
85+
// _14 = const i128_shro(_1, const 7i32) -> bb16;
86+
// ...
87+
// _13 = const i128_shlo(_1, const 6i32) -> bb14;
88+
// END rustc.test_signed.Lower128Bit.after.mir
89+
90+
// START rustc.test_unsigned.Lower128Bit.after.mir
91+
// _2 = const u128_addo(_1, const 1u128) -> bb8;
92+
// ...
93+
// _3 = const u128_subo(_1, const 2u128) -> bb9;
94+
// ...
95+
// _4 = const u128_mulo(_1, const 3u128) -> bb10;
96+
// ...
97+
// _1 = const u128_div(_1, const 4u128) -> bb11;
98+
// ...
99+
// _1 = const u128_rem(_1, const 5u128) -> bb13;
100+
// ...
101+
// _8 = const u128_shro(_1, const 7i32) -> bb14;
102+
// ...
103+
// _7 = const i128_shlo(_1, const 6i32) -> bb12;
104+
// END rustc.test_unsigned.Lower128Bit.after.mir

0 commit comments

Comments
 (0)