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