Skip to content

Commit 94cd4f1

Browse files
committed
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.
1 parent 9491f18 commit 94cd4f1

File tree

5 files changed

+74
-5
lines changed

5 files changed

+74
-5
lines changed

src/librustc_codegen_llvm/builder.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,56 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
652652
unsafe { llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty, UNNAMED) }
653653
}
654654

655+
fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
656+
if self.sess().target.target.arch == "wasm32"
657+
&& self
658+
.sess()
659+
.target_features
660+
.contains(&rustc_span::symbol::Symbol::intern("nontrapping-fptoint"))
661+
{
662+
let src_ty = self.cx.val_ty(val);
663+
let float_width = self.cx.float_width(src_ty);
664+
let int_width = self.cx.int_width(dest_ty);
665+
let name = match (int_width, float_width) {
666+
(32, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f32"),
667+
(32, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f64"),
668+
(64, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f32"),
669+
(64, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f64"),
670+
_ => None,
671+
};
672+
if let Some(name) = name {
673+
let intrinsic = self.get_intrinsic(name);
674+
return Some(self.call(intrinsic, &[val], None));
675+
}
676+
}
677+
None
678+
}
679+
680+
fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
681+
if self.sess().target.target.arch == "wasm32"
682+
&& self
683+
.sess()
684+
.target_features
685+
.contains(&rustc_span::symbol::Symbol::intern("nontrapping-fptoint"))
686+
{
687+
let src_ty = self.cx.val_ty(val);
688+
let float_width = self.cx.float_width(src_ty);
689+
let int_width = self.cx.int_width(dest_ty);
690+
let name = match (int_width, float_width) {
691+
(32, 32) => Some("llvm.wasm.trunc.saturate.signed.i32.f32"),
692+
(32, 64) => Some("llvm.wasm.trunc.saturate.signed.i32.f64"),
693+
(64, 32) => Some("llvm.wasm.trunc.saturate.signed.i64.f32"),
694+
(64, 64) => Some("llvm.wasm.trunc.saturate.signed.i64.f64"),
695+
_ => None,
696+
};
697+
if let Some(name) = name {
698+
let intrinsic = self.get_intrinsic(name);
699+
return Some(self.call(intrinsic, &[val], None));
700+
}
701+
}
702+
None
703+
}
704+
655705
fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
656706
unsafe { llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty, UNNAMED) }
657707
}

src/librustc_codegen_llvm/context.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,15 @@ impl CodegenCx<'b, 'tcx> {
482482
t_v8f64: t_f64, 8;
483483
}
484484

485+
ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f32", fn(t_f32) -> t_i32);
486+
ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f64", fn(t_f64) -> t_i32);
487+
ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f32", fn(t_f32) -> t_i64);
488+
ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f64", fn(t_f64) -> t_i64);
489+
ifn!("llvm.wasm.trunc.saturate.signed.i32.f32", fn(t_f32) -> t_i32);
490+
ifn!("llvm.wasm.trunc.saturate.signed.i32.f64", fn(t_f64) -> t_i32);
491+
ifn!("llvm.wasm.trunc.saturate.signed.i64.f32", fn(t_f32) -> t_i64);
492+
ifn!("llvm.wasm.trunc.saturate.signed.i64.f64", fn(t_f64) -> t_i64);
493+
485494
ifn!("llvm.trap", fn() -> void);
486495
ifn!("llvm.debugtrap", fn() -> void);
487496
ifn!("llvm.frameaddress", fn(t_i32) -> i8p);

src/librustc_codegen_llvm/llvm_util.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,11 @@ const RISCV_WHITELIST: &[(&str, Option<Symbol>)] = &[
250250
("e", Some(sym::riscv_target_feature)),
251251
];
252252

253-
const WASM_WHITELIST: &[(&str, Option<Symbol>)] =
254-
&[("simd128", Some(sym::wasm_target_feature)), ("atomics", Some(sym::wasm_target_feature))];
253+
const WASM_WHITELIST: &[(&str, Option<Symbol>)] = &[
254+
("simd128", Some(sym::wasm_target_feature)),
255+
("atomics", Some(sym::wasm_target_feature)),
256+
("nontrapping-fptoint", Some(sym::wasm_target_feature)), // TODO: Maybe None?
257+
];
255258

256259
/// When rustdoc is running, provide a list of all known features so that all their respective
257260
/// primitives may be documented.

src/librustc_codegen_ssa/mir/rvalue.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -774,12 +774,17 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
774774
float_ty: Bx::Type,
775775
int_ty: Bx::Type,
776776
) -> Bx::Value {
777-
let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
778-
779777
if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts {
780-
return fptosui_result;
778+
return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
779+
}
780+
781+
let try_sat_result = if signed { bx.fptosi_sat(x, int_ty) } else { bx.fptoui_sat(x, int_ty) };
782+
if let Some(try_sat_result) = try_sat_result {
783+
return try_sat_result;
781784
}
782785

786+
let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
787+
783788
let int_width = bx.cx().int_width(int_ty);
784789
let float_width = bx.cx().float_width(float_ty);
785790
// LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the

src/librustc_codegen_ssa/traits/builder.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ pub trait BuilderMethods<'a, 'tcx>:
156156

157157
fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
158158
fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
159+
fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>;
160+
fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>;
159161
fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
160162
fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
161163
fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;

0 commit comments

Comments
 (0)