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