diff options
| author | Christopher Serr <christopher.serr@gmail.com> | 2020-06-25 17:05:12 +0200 |
|---|---|---|
| committer | Christopher Serr <christopher.serr@gmail.com> | 2020-07-02 09:32:03 +0200 |
| commit | 94cd4f15d6479756af6e03a31a204bd588805cbd (patch) | |
| tree | d5265786cf5d4b4cae23f24ac84fea1981009d76 | |
| parent | 9491f18c5de3ff1c4bf9c3fdacf52d9859e26f7c (diff) | |
| download | rust-94cd4f15d6479756af6e03a31a204bd588805cbd.tar.gz rust-94cd4f15d6479756af6e03a31a204bd588805cbd.zip | |
Use WASM's saturating casts if they are available
WebAssembly supports saturating floating point to integer casts behind a target feature. The feature is already available on many browsers. Beginning with 1.45 Rust will start defining the behavior of floating point to integer casts to be saturating as well. For this Rust constructs additional checks on top of the `fptoui` / `fptosi` instructions it emits. Here we introduce the possibility for the codegen backend to construct saturating casts itself and only fall back to constructing the checks ourselves if that is not possible.
| -rw-r--r-- | src/librustc_codegen_llvm/builder.rs | 50 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/context.rs | 9 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/llvm_util.rs | 7 | ||||
| -rw-r--r-- | src/librustc_codegen_ssa/mir/rvalue.rs | 11 | ||||
| -rw-r--r-- | src/librustc_codegen_ssa/traits/builder.rs | 2 |
5 files changed, 74 insertions, 5 deletions
diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index ba285b5ef38..a6b55fee870 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -652,6 +652,56 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty, UNNAMED) } } + fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { + if self.sess().target.target.arch == "wasm32" + && self + .sess() + .target_features + .contains(&rustc_span::symbol::Symbol::intern("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.saturate.unsigned.i32.f32"), + (32, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f64"), + (64, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f32"), + (64, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f64"), + _ => None, + }; + if let Some(name) = name { + let intrinsic = self.get_intrinsic(name); + return Some(self.call(intrinsic, &[val], None)); + } + } + None + } + + fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { + if self.sess().target.target.arch == "wasm32" + && self + .sess() + .target_features + .contains(&rustc_span::symbol::Symbol::intern("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.saturate.signed.i32.f32"), + (32, 64) => Some("llvm.wasm.trunc.saturate.signed.i32.f64"), + (64, 32) => Some("llvm.wasm.trunc.saturate.signed.i64.f32"), + (64, 64) => Some("llvm.wasm.trunc.saturate.signed.i64.f64"), + _ => None, + }; + if let Some(name) = name { + let intrinsic = self.get_intrinsic(name); + return Some(self.call(intrinsic, &[val], None)); + } + } + None + } + fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty, UNNAMED) } } diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 7ff5ac5cbdc..896d3c1de62 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -482,6 +482,15 @@ impl CodegenCx<'b, 'tcx> { t_v8f64: t_f64, 8; } + ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f32", fn(t_f32) -> t_i32); + ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f64", fn(t_f64) -> t_i32); + ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f32", fn(t_f32) -> t_i64); + ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.wasm.trunc.saturate.signed.i32.f32", fn(t_f32) -> t_i32); + ifn!("llvm.wasm.trunc.saturate.signed.i32.f64", fn(t_f64) -> t_i32); + ifn!("llvm.wasm.trunc.saturate.signed.i64.f32", fn(t_f32) -> t_i64); + ifn!("llvm.wasm.trunc.saturate.signed.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.trap", fn() -> void); ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> i8p); diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs index 80278bb9f53..5ec93b0d403 100644 --- a/src/librustc_codegen_llvm/llvm_util.rs +++ b/src/librustc_codegen_llvm/llvm_util.rs @@ -250,8 +250,11 @@ const RISCV_WHITELIST: &[(&str, Option<Symbol>)] = &[ ("e", Some(sym::riscv_target_feature)), ]; -const WASM_WHITELIST: &[(&str, Option<Symbol>)] = - &[("simd128", Some(sym::wasm_target_feature)), ("atomics", Some(sym::wasm_target_feature))]; +const WASM_WHITELIST: &[(&str, Option<Symbol>)] = &[ + ("simd128", Some(sym::wasm_target_feature)), + ("atomics", Some(sym::wasm_target_feature)), + ("nontrapping-fptoint", Some(sym::wasm_target_feature)), // TODO: Maybe None? +]; /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 57f72b1065d..4b2be7b5321 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -774,12 +774,17 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( float_ty: Bx::Type, int_ty: Bx::Type, ) -> Bx::Value { - let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { - return fptosui_result; + return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; + } + + let try_sat_result = if signed { bx.fptosi_sat(x, int_ty) } else { bx.fptoui_sat(x, int_ty) }; + if let Some(try_sat_result) = try_sat_result { + return try_sat_result; } + let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; + let int_width = bx.cx().int_width(int_ty); let float_width = bx.cx().float_width(float_ty); // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index 7ffc9f15bff..d33d6857bc0 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs @@ -156,6 +156,8 @@ pub trait BuilderMethods<'a, 'tcx>: fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>; + fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>; fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; |
