Skip to content

Commit 5f44c65

Browse files
committed
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 #45676 @est31 @nagisa
2 parents 71da1c2 + 42208c1 commit 5f44c65

File tree

7 files changed

+536
-0
lines changed

7 files changed

+536
-0
lines changed

src/librustc/middle/lang_items.rs

+28
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,34 @@ 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+
U128AddFnLangItem, "u128_add", u128_add_fn;
317+
I128SubFnLangItem, "i128_sub", i128_sub_fn;
318+
U128SubFnLangItem, "u128_sub", u128_sub_fn;
319+
I128MulFnLangItem, "i128_mul", i128_mul_fn;
320+
U128MulFnLangItem, "u128_mul", u128_mul_fn;
321+
I128DivFnLangItem, "i128_div", i128_div_fn;
322+
U128DivFnLangItem, "u128_div", u128_div_fn;
323+
I128RemFnLangItem, "i128_rem", i128_rem_fn;
324+
U128RemFnLangItem, "u128_rem", u128_rem_fn;
325+
I128ShlFnLangItem, "i128_shl", i128_shl_fn;
326+
U128ShlFnLangItem, "u128_shl", u128_shl_fn;
327+
I128ShrFnLangItem, "i128_shr", i128_shr_fn;
328+
U128ShrFnLangItem, "u128_shr", u128_shr_fn;
329+
// And overflow versions for the operators that are checkable.
330+
// While MIR calls these Checked*, they return (T,bool), not Option<T>.
331+
I128AddoFnLangItem, "i128_addo", i128_addo_fn;
332+
U128AddoFnLangItem, "u128_addo", u128_addo_fn;
333+
I128SuboFnLangItem, "i128_subo", i128_subo_fn;
334+
U128SuboFnLangItem, "u128_subo", u128_subo_fn;
335+
I128MuloFnLangItem, "i128_mulo", i128_mulo_fn;
336+
U128MuloFnLangItem, "u128_mulo", u128_mulo_fn;
337+
I128ShloFnLangItem, "i128_shlo", i128_shlo_fn;
338+
U128ShloFnLangItem, "u128_shlo", u128_shlo_fn;
339+
I128ShroFnLangItem, "i128_shro", i128_shro_fn;
340+
U128ShroFnLangItem, "u128_shro", u128_shro_fn;
313341
}
314342

315343
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
@@ -1144,6 +1144,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
11441144
saturating_float_casts: bool = (false, parse_bool, [TRACKED],
11451145
"make float->int casts UB-free: numbers outside the integer type's range are clipped to \
11461146
the max/min integer respectively, and NaN is mapped to 0"),
1147+
lower_128bit_ops: bool = (false, parse_bool, [TRACKED],
1148+
"rewrite operators on i128 and u128 into lang item calls (typically provided \
1149+
by compiler-builtins) so translation doesn't need to support them"),
11471150
}
11481151

11491152
pub fn default_lib_output() -> CrateType {
+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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 (lang_item, rhs_kind) =
45+
if let Some((lang_item, rhs_kind)) =
46+
lower_to(&block.statements[i], local_decls, tcx)
47+
{
48+
(lang_item, rhs_kind)
49+
} else {
50+
continue;
51+
};
52+
53+
let rhs_override_ty = rhs_kind.ty(tcx);
54+
let cast_local =
55+
match rhs_override_ty {
56+
None => None,
57+
Some(ty) => {
58+
let local_decl = LocalDecl::new_internal(
59+
ty, block.statements[i].source_info.span);
60+
Some(local_decls.push(local_decl))
61+
},
62+
};
63+
64+
let storage_dead = cast_local.map(|local| {
65+
Statement {
66+
source_info: block.statements[i].source_info,
67+
kind: StatementKind::StorageDead(local),
68+
}
69+
});
70+
let after_call = BasicBlockData {
71+
statements: storage_dead.into_iter()
72+
.chain(block.statements.drain((i+1)..)).collect(),
73+
is_cleanup: block.is_cleanup,
74+
terminator: block.terminator.take(),
75+
};
76+
77+
let bin_statement = block.statements.pop().unwrap();
78+
let (source_info, lvalue, lhs, mut rhs) = match bin_statement {
79+
Statement {
80+
source_info,
81+
kind: StatementKind::Assign(
82+
lvalue,
83+
Rvalue::BinaryOp(_, lhs, rhs))
84+
} => (source_info, lvalue, lhs, rhs),
85+
Statement {
86+
source_info,
87+
kind: StatementKind::Assign(
88+
lvalue,
89+
Rvalue::CheckedBinaryOp(_, lhs, rhs))
90+
} => (source_info, lvalue, lhs, rhs),
91+
_ => bug!("Statement doesn't match pattern any more?"),
92+
};
93+
94+
if let Some(local) = cast_local {
95+
block.statements.push(Statement {
96+
source_info: source_info,
97+
kind: StatementKind::StorageLive(local),
98+
});
99+
block.statements.push(Statement {
100+
source_info: source_info,
101+
kind: StatementKind::Assign(
102+
Lvalue::Local(local),
103+
Rvalue::Cast(
104+
CastKind::Misc,
105+
rhs,
106+
rhs_override_ty.unwrap())),
107+
});
108+
rhs = Operand::Consume(Lvalue::Local(local));
109+
}
110+
111+
let call_did = check_lang_item_type(
112+
lang_item, &lvalue, &lhs, &rhs, local_decls, tcx);
113+
114+
let bb = BasicBlock::new(cur_len + new_blocks.len());
115+
new_blocks.push(after_call);
116+
117+
block.terminator =
118+
Some(Terminator {
119+
source_info,
120+
kind: TerminatorKind::Call {
121+
func: Operand::function_handle(tcx, call_did,
122+
Slice::empty(), source_info.span),
123+
args: vec![lhs, rhs],
124+
destination: Some((lvalue, bb)),
125+
cleanup: None,
126+
},
127+
});
128+
}
129+
}
130+
131+
basic_blocks.extend(new_blocks);
132+
}
133+
}
134+
135+
fn check_lang_item_type<'a, 'tcx, D>(
136+
lang_item: LangItem,
137+
lvalue: &Lvalue<'tcx>,
138+
lhs: &Operand<'tcx>,
139+
rhs: &Operand<'tcx>,
140+
local_decls: &D,
141+
tcx: TyCtxt<'a, 'tcx, 'tcx>)
142+
-> DefId
143+
where D: HasLocalDecls<'tcx>
144+
{
145+
let did = tcx.require_lang_item(lang_item);
146+
let poly_sig = tcx.fn_sig(did);
147+
let sig = tcx.no_late_bound_regions(&poly_sig).unwrap();
148+
let lhs_ty = lhs.ty(local_decls, tcx);
149+
let rhs_ty = rhs.ty(local_decls, tcx);
150+
let lvalue_ty = lvalue.ty(local_decls, tcx).to_ty(tcx);
151+
let expected = [lhs_ty, rhs_ty, lvalue_ty];
152+
assert_eq!(sig.inputs_and_output[..], expected,
153+
"lang item {}", tcx.def_symbol_name(did));
154+
did
155+
}
156+
157+
fn lower_to<'a, 'tcx, D>(statement: &Statement<'tcx>, local_decls: &D, tcx: TyCtxt<'a, 'tcx, 'tcx>)
158+
-> Option<(LangItem, RhsKind)>
159+
where D: HasLocalDecls<'tcx>
160+
{
161+
match statement.kind {
162+
StatementKind::Assign(_, Rvalue::BinaryOp(bin_op, ref lhs, _)) => {
163+
let ty = lhs.ty(local_decls, tcx);
164+
if let Some(is_signed) = sign_of_128bit(ty) {
165+
return item_for_op(bin_op, is_signed);
166+
}
167+
},
168+
StatementKind::Assign(_, Rvalue::CheckedBinaryOp(bin_op, ref lhs, _)) => {
169+
let ty = lhs.ty(local_decls, tcx);
170+
if let Some(is_signed) = sign_of_128bit(ty) {
171+
return item_for_checked_op(bin_op, is_signed);
172+
}
173+
},
174+
_ => {},
175+
}
176+
None
177+
}
178+
179+
#[derive(Copy, Clone)]
180+
enum RhsKind {
181+
Unchanged,
182+
ForceU128,
183+
ForceU32,
184+
}
185+
186+
impl RhsKind {
187+
fn ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option<Ty<'tcx>> {
188+
match *self {
189+
RhsKind::Unchanged => None,
190+
RhsKind::ForceU128 => Some(tcx.types.u128),
191+
RhsKind::ForceU32 => Some(tcx.types.u32),
192+
}
193+
}
194+
}
195+
196+
fn sign_of_128bit(ty: Ty) -> Option<bool> {
197+
match ty.sty {
198+
TypeVariants::TyInt(syntax::ast::IntTy::I128) => Some(true),
199+
TypeVariants::TyUint(syntax::ast::UintTy::U128) => Some(false),
200+
_ => None,
201+
}
202+
}
203+
204+
fn item_for_op(bin_op: BinOp, is_signed: bool) -> Option<(LangItem, RhsKind)> {
205+
let i = match (bin_op, is_signed) {
206+
(BinOp::Add, true) => (LangItem::I128AddFnLangItem, RhsKind::Unchanged),
207+
(BinOp::Add, false) => (LangItem::U128AddFnLangItem, RhsKind::Unchanged),
208+
(BinOp::Sub, true) => (LangItem::I128SubFnLangItem, RhsKind::Unchanged),
209+
(BinOp::Sub, false) => (LangItem::U128SubFnLangItem, RhsKind::Unchanged),
210+
(BinOp::Mul, true) => (LangItem::I128MulFnLangItem, RhsKind::Unchanged),
211+
(BinOp::Mul, false) => (LangItem::U128MulFnLangItem, RhsKind::Unchanged),
212+
(BinOp::Div, true) => (LangItem::I128DivFnLangItem, RhsKind::Unchanged),
213+
(BinOp::Div, false) => (LangItem::U128DivFnLangItem, RhsKind::Unchanged),
214+
(BinOp::Rem, true) => (LangItem::I128RemFnLangItem, RhsKind::Unchanged),
215+
(BinOp::Rem, false) => (LangItem::U128RemFnLangItem, RhsKind::Unchanged),
216+
(BinOp::Shl, true) => (LangItem::I128ShlFnLangItem, RhsKind::ForceU32),
217+
(BinOp::Shl, false) => (LangItem::U128ShlFnLangItem, RhsKind::ForceU32),
218+
(BinOp::Shr, true) => (LangItem::I128ShrFnLangItem, RhsKind::ForceU32),
219+
(BinOp::Shr, false) => (LangItem::U128ShrFnLangItem, RhsKind::ForceU32),
220+
_ => return None,
221+
};
222+
Some(i)
223+
}
224+
225+
fn item_for_checked_op(bin_op: BinOp, is_signed: bool) -> Option<(LangItem, RhsKind)> {
226+
let i = match (bin_op, is_signed) {
227+
(BinOp::Add, true) => (LangItem::I128AddoFnLangItem, RhsKind::Unchanged),
228+
(BinOp::Add, false) => (LangItem::U128AddoFnLangItem, RhsKind::Unchanged),
229+
(BinOp::Sub, true) => (LangItem::I128SuboFnLangItem, RhsKind::Unchanged),
230+
(BinOp::Sub, false) => (LangItem::U128SuboFnLangItem, RhsKind::Unchanged),
231+
(BinOp::Mul, true) => (LangItem::I128MuloFnLangItem, RhsKind::Unchanged),
232+
(BinOp::Mul, false) => (LangItem::U128MuloFnLangItem, RhsKind::Unchanged),
233+
(BinOp::Shl, true) => (LangItem::I128ShloFnLangItem, RhsKind::ForceU128),
234+
(BinOp::Shl, false) => (LangItem::U128ShloFnLangItem, RhsKind::ForceU128),
235+
(BinOp::Shr, true) => (LangItem::I128ShroFnLangItem, RhsKind::ForceU128),
236+
(BinOp::Shr, false) => (LangItem::U128ShroFnLangItem, RhsKind::ForceU128),
237+
_ => bug!("That should be all the checked ones?"),
238+
};
239+
Some(i)
240+
}

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,

0 commit comments

Comments
 (0)