diff options
Diffstat (limited to 'src/librustc_codegen_llvm')
| -rw-r--r-- | src/librustc_codegen_llvm/builder.rs | 61 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/intrinsic.rs | 70 |
2 files changed, 76 insertions, 55 deletions
diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 8a1bb258d42..1124e91bf71 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -703,11 +703,67 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { None } + fn fptosui_may_trap(&self, val: &'ll Value, dest_ty: &'ll Type) -> bool { + // Most of the time we'll be generating the `fptosi` or `fptoui` + // instruction for floating-point-to-integer conversions. These + // instructions by definition in LLVM do not trap. For the WebAssembly + // target, however, we'll lower in some cases to intrinsic calls instead + // which may trap. If we detect that this is a situation where we'll be + // using the intrinsics then we report that the call map trap, which + // callers might need to handle. + if !self.wasm_and_missing_nontrapping_fptoint() { + return false; + } + let src_ty = self.cx.val_ty(val); + let float_width = self.cx.float_width(src_ty); + let int_width = self.cx.int_width(dest_ty); + match (int_width, float_width) { + (32, 32) | (32, 64) | (64, 32) | (64, 64) => true, + _ => false, + } + } + fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { + // When we can, use the native wasm intrinsics which have tighter + // codegen. Note that this has a semantic difference in that the + // intrinsic can trap whereas `fptoui` never traps. That difference, + // however, is handled by `fptosui_may_trap` above. + if self.wasm_and_missing_nontrapping_fptoint() { + let src_ty = self.cx.val_ty(val); + let float_width = self.cx.float_width(src_ty); + let int_width = self.cx.int_width(dest_ty); + let name = match (int_width, float_width) { + (32, 32) => Some("llvm.wasm.trunc.unsigned.i32.f32"), + (32, 64) => Some("llvm.wasm.trunc.unsigned.i32.f64"), + (64, 32) => Some("llvm.wasm.trunc.unsigned.i64.f32"), + (64, 64) => Some("llvm.wasm.trunc.unsigned.i64.f64"), + _ => None, + }; + if let Some(name) = name { + let intrinsic = self.get_intrinsic(name); + return self.call(intrinsic, &[val], None); + } + } unsafe { llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty, UNNAMED) } } fn fptosi(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { + if self.wasm_and_missing_nontrapping_fptoint() { + let src_ty = self.cx.val_ty(val); + let float_width = self.cx.float_width(src_ty); + let int_width = self.cx.int_width(dest_ty); + let name = match (int_width, float_width) { + (32, 32) => Some("llvm.wasm.trunc.signed.i32.f32"), + (32, 64) => Some("llvm.wasm.trunc.signed.i32.f64"), + (64, 32) => Some("llvm.wasm.trunc.signed.i64.f32"), + (64, 64) => Some("llvm.wasm.trunc.signed.i64.f64"), + _ => None, + }; + if let Some(name) = name { + let intrinsic = self.get_intrinsic(name); + return self.call(intrinsic, &[val], None); + } + } unsafe { llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty, UNNAMED) } } @@ -1349,4 +1405,9 @@ impl Builder<'a, 'll, 'tcx> { llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); } } + + fn wasm_and_missing_nontrapping_fptoint(&self) -> bool { + self.sess().target.target.arch == "wasm32" + && !self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) + } } diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 728af7b0a8c..e3a308181b6 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -634,22 +634,19 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } sym::float_to_int_unchecked => { - let float_width = match float_type_width(arg_tys[0]) { - Some(width) => width, - None => { - span_invalid_monomorphization_error( - tcx.sess, - span, - &format!( - "invalid monomorphization of `float_to_int_unchecked` \ + if float_type_width(arg_tys[0]).is_none() { + span_invalid_monomorphization_error( + tcx.sess, + span, + &format!( + "invalid monomorphization of `float_to_int_unchecked` \ intrinsic: expected basic float type, \ found `{}`", - arg_tys[0] - ), - ); - return; - } - }; + arg_tys[0] + ), + ); + return; + } let (width, signed) = match int_type_width_signed(ret_ty, self.cx) { Some(pair) => pair, None => { @@ -666,48 +663,11 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { return; } }; - - // The LLVM backend can reorder and speculate `fptosi` and - // `fptoui`, so on WebAssembly the codegen for this instruction - // is quite heavyweight. To avoid this heavyweight codegen we - // instead use the raw wasm intrinsics which will lower to one - // instruction in WebAssembly (`iNN.trunc_fMM_{s,u}`). This one - // instruction will trap if the operand is out of bounds, but - // that's ok since this intrinsic is UB if the operands are out - // of bounds, so the behavior can be different on WebAssembly - // than other targets. - // - // Note, however, that when the `nontrapping-fptoint` feature is - // enabled in LLVM then LLVM will lower `fptosi` to - // `iNN.trunc_sat_fMM_{s,u}`, so if that's the case we don't - // bother with intrinsics. - let mut result = None; - if self.sess().target.target.arch == "wasm32" - && !self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) - { - let name = match (width, float_width, signed) { - (32, 32, true) => Some("llvm.wasm.trunc.signed.i32.f32"), - (32, 64, true) => Some("llvm.wasm.trunc.signed.i32.f64"), - (64, 32, true) => Some("llvm.wasm.trunc.signed.i64.f32"), - (64, 64, true) => Some("llvm.wasm.trunc.signed.i64.f64"), - (32, 32, false) => Some("llvm.wasm.trunc.unsigned.i32.f32"), - (32, 64, false) => Some("llvm.wasm.trunc.unsigned.i32.f64"), - (64, 32, false) => Some("llvm.wasm.trunc.unsigned.i64.f32"), - (64, 64, false) => Some("llvm.wasm.trunc.unsigned.i64.f64"), - _ => None, - }; - if let Some(name) = name { - let intrinsic = self.get_intrinsic(name); - result = Some(self.call(intrinsic, &[args[0].immediate()], None)); - } + if signed { + self.fptosi(args[0].immediate(), self.cx.type_ix(width)) + } else { + self.fptoui(args[0].immediate(), self.cx.type_ix(width)) } - result.unwrap_or_else(|| { - if signed { - self.fptosi(args[0].immediate(), self.cx.type_ix(width)) - } else { - self.fptoui(args[0].immediate(), self.cx.type_ix(width)) - } - }) } sym::discriminant_value => { |
