about summary refs log tree commit diff
path: root/src/librustc_codegen_ssa
diff options
context:
space:
mode:
authorpawanbisht62 <36775517+pawanbisht62@users.noreply.github.com>2020-08-06 17:00:51 +0530
committerGitHub <noreply@github.com>2020-08-06 17:00:51 +0530
commitbac939edcf2137f625cfd7b56dfa4aa7b814868f (patch)
treebbeab2a9f6869b0be809a8e7029a62a77c8e22da /src/librustc_codegen_ssa
parentfdfbd89946ca34d12eec658d111ce9a85cd23df0 (diff)
parent3cfc7fe78eccc754b16981704a098d7bd520e2fd (diff)
Merge branch 'master' into feature/incorporate-tracing
Diffstat (limited to 'src/librustc_codegen_ssa')
-rw-r--r--src/librustc_codegen_ssa/coverageinfo/map.rs184
-rw-r--r--src/librustc_codegen_ssa/coverageinfo/mod.rs1
-rw-r--r--src/librustc_codegen_ssa/mir/rvalue.rs167
-rw-r--r--src/librustc_codegen_ssa/traits/builder.rs1
-rw-r--r--src/librustc_codegen_ssa/traits/coverageinfo.rs15
5 files changed, 177 insertions, 191 deletions
diff --git a/src/librustc_codegen_ssa/coverageinfo/map.rs b/src/librustc_codegen_ssa/coverageinfo/map.rs
index 72138065a90..7f6841f9daa 100644
--- a/src/librustc_codegen_ssa/coverageinfo/map.rs
+++ b/src/librustc_codegen_ssa/coverageinfo/map.rs
@@ -3,12 +3,8 @@ pub use super::ffi::*;
 use rustc_index::vec::IndexVec;
 use rustc_middle::ty::Instance;
 use rustc_middle::ty::TyCtxt;
-use rustc_span::source_map::{Pos, SourceMap};
-use rustc_span::{BytePos, FileName, Loc, RealFileName};
 
-use std::cmp::{Ord, Ordering};
-use std::fmt;
-use std::path::PathBuf;
+use std::cmp::Ord;
 
 rustc_index::newtype_index! {
     pub struct ExpressionOperandId {
@@ -38,127 +34,35 @@ rustc_index::newtype_index! {
     }
 }
 
-#[derive(Clone, Debug)]
-pub struct Region {
-    start: Loc,
-    end: Loc,
-}
-
-impl Ord for Region {
-    fn cmp(&self, other: &Self) -> Ordering {
-        (&self.start.file.name, &self.start.line, &self.start.col, &self.end.line, &self.end.col)
-            .cmp(&(
-                &other.start.file.name,
-                &other.start.line,
-                &other.start.col,
-                &other.end.line,
-                &other.end.col,
-            ))
-    }
-}
-
-impl PartialOrd for Region {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl PartialEq for Region {
-    fn eq(&self, other: &Self) -> bool {
-        self.start.file.name == other.start.file.name
-            && self.start.line == other.start.line
-            && self.start.col == other.start.col
-            && self.end.line == other.end.line
-            && self.end.col == other.end.col
-    }
-}
-
-impl Eq for Region {}
-
-impl fmt::Display for Region {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let (file_path, start_line, start_col, end_line, end_col) = self.file_start_and_end();
-        write!(f, "{:?}:{}:{} - {}:{}", file_path, start_line, start_col, end_line, end_col)
-    }
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Region<'tcx> {
+    pub file_name: &'tcx str,
+    pub start_line: u32,
+    pub start_col: u32,
+    pub end_line: u32,
+    pub end_col: u32,
 }
 
-impl Region {
-    pub fn new(source_map: &SourceMap, start_byte_pos: u32, end_byte_pos: u32) -> Self {
-        let start = source_map.lookup_char_pos(BytePos::from_u32(start_byte_pos));
-        let end = source_map.lookup_char_pos(BytePos::from_u32(end_byte_pos));
-        assert_eq!(
-            start.file.name, end.file.name,
-            "Region start ({} -> {:?}) and end ({} -> {:?}) don't come from the same source file!",
-            start_byte_pos, start, end_byte_pos, end
-        );
-        Self { start, end }
-    }
-
-    pub fn file_start_and_end<'a>(&'a self) -> (&'a PathBuf, u32, u32, u32, u32) {
-        let start = &self.start;
-        let end = &self.end;
-        match &start.file.name {
-            FileName::Real(RealFileName::Named(path)) => (
-                path,
-                start.line as u32,
-                start.col.to_u32() + 1,
-                end.line as u32,
-                end.col.to_u32() + 1,
-            ),
-            _ => {
-                bug!("start.file.name should be a RealFileName, but it was: {:?}", start.file.name)
-            }
-        }
+impl<'tcx> Region<'tcx> {
+    pub fn new(
+        file_name: &'tcx str,
+        start_line: u32,
+        start_col: u32,
+        end_line: u32,
+        end_col: u32,
+    ) -> Self {
+        Self { file_name, start_line, start_col, end_line, end_col }
     }
 }
 
 #[derive(Clone, Debug)]
-pub struct ExpressionRegion {
+pub struct ExpressionRegion<'tcx> {
     lhs: ExpressionOperandId,
     op: ExprKind,
     rhs: ExpressionOperandId,
-    region: Region,
+    region: Region<'tcx>,
 }
 
-// FIXME(richkadel): There seems to be a problem computing the file location in
-// some cases. I need to investigate this more. When I generate and show coverage
-// for the example binary in the crates.io crate `json5format`, I had a couple of
-// notable problems:
-//
-//   1. I saw a lot of coverage spans in `llvm-cov show` highlighting regions in
-//      various comments (not corresponding to rustdoc code), indicating a possible
-//      problem with the byte_pos-to-source-map implementation.
-//
-//   2. And (perhaps not related) when I build the aforementioned example binary with:
-//      `RUST_FLAGS="-Zinstrument-coverage" cargo build --example formatjson5`
-//      and then run that binary with
-//      `LLVM_PROFILE_FILE="formatjson5.profraw" ./target/debug/examples/formatjson5 \
-//      some.json5` for some reason the binary generates *TWO* `.profraw` files. One
-//      named `default.profraw` and the other named `formatjson5.profraw` (the expected
-//      name, in this case).
-//
-//   3. I think that if I eliminate regions within a function, their region_ids,
-//      referenced in expressions, will be wrong? I think the ids are implied by their
-//      array position in the final coverage map output (IIRC).
-//
-//   4. I suspect a problem (if not the only problem) is the SourceMap is wrong for some
-//      region start/end byte positions. Just like I couldn't get the function hash at
-//      intrinsic codegen time for external crate functions, I think the SourceMap I
-//      have here only applies to the local crate, and I know I have coverages that
-//      reference external crates.
-//
-//          I still don't know if I fixed the hash problem correctly. If external crates
-//          implement the function, can't I use the coverage counters already compiled
-//          into those external crates? (Maybe not for generics and/or maybe not for
-//          macros... not sure. But I need to understand this better.)
-//
-// If the byte range conversion is wrong, fix it. But if it
-// is right, then it is possible for the start and end to be in different files.
-// Can I do something other than ignore coverages that span multiple files?
-//
-// If I can resolve this, remove the "Option<>" result type wrapper
-// `regions_in_file_order()` accordingly.
-
 /// Collects all of the coverage regions associated with (a) injected counters, (b) counter
 /// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
 /// for a given Function. Counters and counter expressions have non-overlapping `id`s because they
@@ -171,19 +75,17 @@ pub struct ExpressionRegion {
 /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
 /// for a gap area is only used as the line execution count if there are no other regions on a
 /// line."
-pub struct FunctionCoverage<'a> {
-    source_map: &'a SourceMap,
+pub struct FunctionCoverage<'tcx> {
     source_hash: u64,
-    counters: IndexVec<CounterValueReference, Option<Region>>,
-    expressions: IndexVec<InjectedExpressionIndex, Option<ExpressionRegion>>,
-    unreachable_regions: Vec<Region>,
+    counters: IndexVec<CounterValueReference, Option<Region<'tcx>>>,
+    expressions: IndexVec<InjectedExpressionIndex, Option<ExpressionRegion<'tcx>>>,
+    unreachable_regions: Vec<Region<'tcx>>,
 }
 
-impl<'a> FunctionCoverage<'a> {
-    pub fn new<'tcx: 'a>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
+impl<'tcx> FunctionCoverage<'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
         let coverageinfo = tcx.coverageinfo(instance.def_id());
         Self {
-            source_map: tcx.sess.source_map(),
             source_hash: 0, // will be set with the first `add_counter()`
             counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
             expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
@@ -194,20 +96,14 @@ impl<'a> FunctionCoverage<'a> {
     /// Adds a code region to be counted by an injected counter intrinsic.
     /// The source_hash (computed during coverage instrumentation) should also be provided, and
     /// should be the same for all counters in a given function.
-    pub fn add_counter(
-        &mut self,
-        source_hash: u64,
-        id: u32,
-        start_byte_pos: u32,
-        end_byte_pos: u32,
-    ) {
+    pub fn add_counter(&mut self, source_hash: u64, id: u32, region: Region<'tcx>) {
         if self.source_hash == 0 {
             self.source_hash = source_hash;
         } else {
             debug_assert_eq!(source_hash, self.source_hash);
         }
         self.counters[CounterValueReference::from(id)]
-            .replace(Region::new(self.source_map, start_byte_pos, end_byte_pos))
+            .replace(region)
             .expect_none("add_counter called with duplicate `id`");
     }
 
@@ -231,8 +127,7 @@ impl<'a> FunctionCoverage<'a> {
         lhs: u32,
         op: ExprKind,
         rhs: u32,
-        start_byte_pos: u32,
-        end_byte_pos: u32,
+        region: Region<'tcx>,
     ) {
         let expression_id = ExpressionOperandId::from(id_descending_from_max);
         let lhs = ExpressionOperandId::from(lhs);
@@ -240,18 +135,13 @@ impl<'a> FunctionCoverage<'a> {
 
         let expression_index = self.expression_index(expression_id);
         self.expressions[expression_index]
-            .replace(ExpressionRegion {
-                lhs,
-                op,
-                rhs,
-                region: Region::new(self.source_map, start_byte_pos, end_byte_pos),
-            })
+            .replace(ExpressionRegion { lhs, op, rhs, region })
             .expect_none("add_counter_expression called with duplicate `id_descending_from_max`");
     }
 
     /// Add a region that will be marked as "unreachable", with a constant "zero counter".
-    pub fn add_unreachable_region(&mut self, start_byte_pos: u32, end_byte_pos: u32) {
-        self.unreachable_regions.push(Region::new(self.source_map, start_byte_pos, end_byte_pos));
+    pub fn add_unreachable_region(&mut self, region: Region<'tcx>) {
+        self.unreachable_regions.push(region)
     }
 
     /// Return the source hash, generated from the HIR node structure, and used to indicate whether
@@ -264,8 +154,8 @@ impl<'a> FunctionCoverage<'a> {
     /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
     /// `CounterMappingRegion`s.
     pub fn get_expressions_and_counter_regions(
-        &'a self,
-    ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a Region)>) {
+        &'tcx self,
+    ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'tcx Region<'tcx>)>) {
         assert!(self.source_hash != 0);
 
         let counter_regions = self.counter_regions();
@@ -277,7 +167,7 @@ impl<'a> FunctionCoverage<'a> {
         (counter_expressions, counter_regions)
     }
 
-    fn counter_regions(&'a self) -> impl Iterator<Item = (Counter, &'a Region)> {
+    fn counter_regions(&'tcx self) -> impl Iterator<Item = (Counter, &'tcx Region<'tcx>)> {
         self.counters.iter_enumerated().filter_map(|(index, entry)| {
             // Option::map() will return None to filter out missing counters. This may happen
             // if, for example, a MIR-instrumented counter is removed during an optimization.
@@ -288,8 +178,8 @@ impl<'a> FunctionCoverage<'a> {
     }
 
     fn expressions_with_regions(
-        &'a self,
-    ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a Region)>) {
+        &'tcx self,
+    ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'tcx Region<'tcx>)>) {
         let mut counter_expressions = Vec::with_capacity(self.expressions.len());
         let mut expression_regions = Vec::with_capacity(self.expressions.len());
         let mut new_indexes =
@@ -350,7 +240,7 @@ impl<'a> FunctionCoverage<'a> {
         (counter_expressions, expression_regions.into_iter())
     }
 
-    fn unreachable_regions(&'a self) -> impl Iterator<Item = (Counter, &'a Region)> {
+    fn unreachable_regions(&'tcx self) -> impl Iterator<Item = (Counter, &'tcx Region<'tcx>)> {
         self.unreachable_regions.iter().map(|region| (Counter::zero(), region))
     }
 
diff --git a/src/librustc_codegen_ssa/coverageinfo/mod.rs b/src/librustc_codegen_ssa/coverageinfo/mod.rs
index 1f0ffd289b1..ff794a75c36 100644
--- a/src/librustc_codegen_ssa/coverageinfo/mod.rs
+++ b/src/librustc_codegen_ssa/coverageinfo/mod.rs
@@ -2,3 +2,4 @@ pub mod ffi;
 pub mod map;
 
 pub use map::ExprKind;
+pub use map::Region;
diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs
index 9c108998bc9..77e94fe3d0a 100644
--- a/src/librustc_codegen_ssa/mir/rvalue.rs
+++ b/src/librustc_codegen_ssa/mir/rvalue.rs
@@ -11,7 +11,7 @@ use rustc_apfloat::{ieee, Float, Round, Status};
 use rustc_hir::lang_items::ExchangeMallocFnLangItem;
 use rustc_middle::mir;
 use rustc_middle::ty::cast::{CastTy, IntTy};
-use rustc_middle::ty::layout::HasTyCtxt;
+use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
 use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
 use rustc_span::source_map::{Span, DUMMY_SP};
 use rustc_span::symbol::sym;
@@ -369,10 +369,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                 bx.inttoptr(usize_llval, ll_t_out)
                             }
                             (CastTy::Float, CastTy::Int(IntTy::I)) => {
-                                cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out)
+                                cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out, cast)
                             }
                             (CastTy::Float, CastTy::Int(_)) => {
-                                cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out)
+                                cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out, cast)
                             }
                             _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty),
                         };
@@ -772,6 +772,7 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     x: Bx::Value,
     float_ty: Bx::Type,
     int_ty: Bx::Type,
+    int_layout: TyAndLayout<'tcx>,
 ) -> Bx::Value {
     if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts {
         return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
@@ -782,8 +783,6 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         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
@@ -870,36 +869,138 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     //     int_ty::MIN and therefore the return value of int_ty::MIN is correct.
     // QED.
 
-    // Step 1 was already performed above.
-
-    // Step 2: We use two comparisons and two selects, with %s1 being the result:
-    //     %less_or_nan = fcmp ult %x, %f_min
-    //     %greater = fcmp olt %x, %f_max
-    //     %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result
-    //     %s1 = select %greater, int_ty::MAX, %s0
-    // Note that %less_or_nan uses an *unordered* comparison. This comparison is true if the
-    // operands are not comparable (i.e., if x is NaN). The unordered comparison ensures that s1
-    // becomes int_ty::MIN if x is NaN.
-    // Performance note: Unordered comparison can be lowered to a "flipped" comparison and a
-    // negation, and the negation can be merged into the select. Therefore, it not necessarily any
-    // more expensive than a ordered ("normal") comparison. Whether these optimizations will be
-    // performed is ultimately up to the backend, but at least x86 does perform them.
-    let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min);
-    let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max);
     let int_max = bx.cx().const_uint_big(int_ty, int_max(signed, int_width));
     let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128);
-    let s0 = bx.select(less_or_nan, int_min, fptosui_result);
-    let s1 = bx.select(greater, int_max, s0);
-
-    // Step 3: NaN replacement.
-    // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN.
-    // Therefore we only need to execute this step for signed integer types.
-    if signed {
-        // LLVM has no isNaN predicate, so we use (x == x) instead
-        let zero = bx.cx().const_uint(int_ty, 0);
-        let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x);
-        bx.select(cmp, s1, zero)
+    let zero = bx.cx().const_uint(int_ty, 0);
+
+    // The codegen here differs quite a bit depending on whether our builder's
+    // `fptosi` and `fptoui` instructions may trap for out-of-bounds values. If
+    // they don't trap then we can start doing everything inline with a
+    // `select` instruction because it's ok to execute `fptosi` and `fptoui`
+    // even if we don't use the results.
+    if !bx.fptosui_may_trap(x, int_ty) {
+        // Step 1 ...
+        let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
+        let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min);
+        let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max);
+
+        // Step 2: We use two comparisons and two selects, with %s1 being the
+        // result:
+        //     %less_or_nan = fcmp ult %x, %f_min
+        //     %greater = fcmp olt %x, %f_max
+        //     %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result
+        //     %s1 = select %greater, int_ty::MAX, %s0
+        // Note that %less_or_nan uses an *unordered* comparison. This
+        // comparison is true if the operands are not comparable (i.e., if x is
+        // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if
+        // x is NaN.
+        //
+        // Performance note: Unordered comparison can be lowered to a "flipped"
+        // comparison and a negation, and the negation can be merged into the
+        // select. Therefore, it not necessarily any more expensive than a
+        // ordered ("normal") comparison. Whether these optimizations will be
+        // performed is ultimately up to the backend, but at least x86 does
+        // perform them.
+        let s0 = bx.select(less_or_nan, int_min, fptosui_result);
+        let s1 = bx.select(greater, int_max, s0);
+
+        // Step 3: NaN replacement.
+        // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN.
+        // Therefore we only need to execute this step for signed integer types.
+        if signed {
+            // LLVM has no isNaN predicate, so we use (x == x) instead
+            let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x);
+            bx.select(cmp, s1, zero)
+        } else {
+            s1
+        }
     } else {
-        s1
+        // In this case we cannot execute `fptosi` or `fptoui` and then later
+        // discard the result. The builder is telling us that these instructions
+        // will trap on out-of-bounds values, so we need to use basic blocks and
+        // control flow to avoid executing the `fptosi` and `fptoui`
+        // instructions.
+        //
+        // The general idea of what we're constructing here is, for f64 -> i32:
+        //
+        //      ;; block so far... %0 is the argument
+        //      %result = alloca i32, align 4
+        //      %inbound_lower = fcmp oge double %0, 0xC1E0000000000000
+        //      %inbound_upper = fcmp ole double %0, 0x41DFFFFFFFC00000
+        //      ;; match (inbound_lower, inbound_upper) {
+        //      ;;     (true, true) => %0 can be converted without trapping
+        //      ;;     (false, false) => %0 is a NaN
+        //      ;;     (true, false) => %0 is too large
+        //      ;;     (false, true) => %0 is too small
+        //      ;; }
+        //      ;;
+        //      ;; The (true, true) check, go to %convert if so.
+        //      %inbounds = and i1 %inbound_lower, %inbound_upper
+        //      br i1 %inbounds, label %convert, label %specialcase
+        //
+        //  convert:
+        //      %cvt = call i32 @llvm.wasm.trunc.signed.i32.f64(double %0)
+        //      store i32 %cvt, i32* %result, align 4
+        //      br label %done
+        //
+        //  specialcase:
+        //      ;; Handle the cases where the number is NaN, too large or too small
+        //
+        //      ;; Either (true, false) or (false, true)
+        //      %is_not_nan = or i1 %inbound_lower, %inbound_upper
+        //      ;; Figure out which saturated value we are interested in if not `NaN`
+        //      %saturated = select i1 %inbound_lower, i32 2147483647, i32 -2147483648
+        //      ;; Figure out between saturated and NaN representations
+        //      %result_nan = select i1 %is_not_nan, i32 %saturated, i32 0
+        //      store i32 %result_nan, i32* %result, align 4
+        //      br label %done
+        //
+        //  done:
+        //      %r = load i32, i32* %result, align 4
+        //      ;; ...
+        let done = bx.build_sibling_block("float_cast_done");
+        let mut convert = bx.build_sibling_block("float_cast_convert");
+        let mut specialcase = bx.build_sibling_block("float_cast_specialcase");
+
+        let result = PlaceRef::alloca(bx, int_layout);
+        result.storage_live(bx);
+
+        // Use control flow to figure out whether we can execute `fptosi` in a
+        // basic block, or whether we go to a different basic block to implement
+        // the saturating logic.
+        let inbound_lower = bx.fcmp(RealPredicate::RealOGE, x, f_min);
+        let inbound_upper = bx.fcmp(RealPredicate::RealOLE, x, f_max);
+        let inbounds = bx.and(inbound_lower, inbound_upper);
+        bx.cond_br(inbounds, convert.llbb(), specialcase.llbb());
+
+        // Translation of the `convert` basic block
+        let cvt = if signed { convert.fptosi(x, int_ty) } else { convert.fptoui(x, int_ty) };
+        convert.store(cvt, result.llval, result.align);
+        convert.br(done.llbb());
+
+        // Translation of the `specialcase` basic block. Note that like above
+        // we try to be a bit clever here for unsigned conversions. In those
+        // cases the `int_min` is zero so we don't need two select instructions,
+        // just one to choose whether we need `int_max` or not. If
+        // `inbound_lower` is true then we're guaranteed to not be `NaN` and
+        // since we're greater than zero we must be saturating to `int_max`. If
+        // `inbound_lower` is false then we're either NaN or less than zero, so
+        // we saturate to zero.
+        let result_nan = if signed {
+            let is_not_nan = specialcase.or(inbound_lower, inbound_upper);
+            let saturated = specialcase.select(inbound_lower, int_max, int_min);
+            specialcase.select(is_not_nan, saturated, zero)
+        } else {
+            specialcase.select(inbound_lower, int_max, int_min)
+        };
+        specialcase.store(result_nan, result.llval, result.align);
+        specialcase.br(done.llbb());
+
+        // Translation of the `done` basic block, positioning ourselves to
+        // continue from that point as well.
+        *bx = done;
+        let ret = bx.load(result.llval, result.align);
+        result.storage_dead(bx);
+        ret
     }
 }
diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs
index 65eb70e173e..4e11ef5fd6e 100644
--- a/src/librustc_codegen_ssa/traits/builder.rs
+++ b/src/librustc_codegen_ssa/traits/builder.rs
@@ -160,6 +160,7 @@ pub trait BuilderMethods<'a, 'tcx>:
     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 fptosui_may_trap(&self, val: Self::Value, dest_ty: Self::Type) -> bool;
     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;
diff --git a/src/librustc_codegen_ssa/traits/coverageinfo.rs b/src/librustc_codegen_ssa/traits/coverageinfo.rs
index db1d86c974e..2b5878f46bc 100644
--- a/src/librustc_codegen_ssa/traits/coverageinfo.rs
+++ b/src/librustc_codegen_ssa/traits/coverageinfo.rs
@@ -1,5 +1,5 @@
 use super::BackendTypes;
-use crate::coverageinfo::ExprKind;
+use crate::coverageinfo::{ExprKind, Region};
 use rustc_middle::ty::Instance;
 
 pub trait CoverageInfoMethods: BackendTypes {
@@ -12,8 +12,7 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
         instance: Instance<'tcx>,
         function_source_hash: u64,
         index: u32,
-        start_byte_pos: u32,
-        end_byte_pos: u32,
+        region: Region<'tcx>,
     );
 
     fn add_counter_expression_region(
@@ -23,14 +22,8 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
         lhs: u32,
         op: ExprKind,
         rhs: u32,
-        start_byte_pos: u32,
-        end_byte_pos: u32,
+        region: Region<'tcx>,
     );
 
-    fn add_unreachable_region(
-        &mut self,
-        instance: Instance<'tcx>,
-        start_byte_pos: u32,
-        end_byte_pos: u32,
-    );
+    fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: Region<'tcx>);
 }