-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Please review commit by commit, as each commit propagates an upstream change: - `SourceRegion` was moved from MIR to the LLVM backend and made private, I recreated the type and functionality locally to preserve our handling of regions in coverage assertions, - Propagated changes to the representation of uninhabited enums (with no variants), - Propagated remove of `RValue::Len(place)` from MIR (still present in stable MIR, so I used `PtrMetadata(Copy(place))` to translate Len(place) back from StableMIR to MIR). - Propagated addition of `UnsafeBinder` enum variant (WIP in rustc, no clear semantics yet, left as TODO) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Remi Delmas <[email protected]>
- Loading branch information
1 parent
4d477f6
commit bd6ca46
Showing
21 changed files
with
187 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
135 changes: 135 additions & 0 deletions
135
kani-compiler/src/codegen_cprover_gotoc/codegen/source_region.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// Copyright Kani Contributors | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
||
//! This file contains our own local version of | ||
//! the `Span` to `CoverageRegion` conversion defined in | ||
//! https://github.com/rust-lang/rust/tree/master/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs | ||
use rustc_middle::mir::coverage::FunctionCoverageInfo; | ||
use rustc_span::Span; | ||
use rustc_span::source_map::SourceMap; | ||
use rustc_span::{BytePos, SourceFile}; | ||
use std::fmt::{self, Debug, Formatter}; | ||
use tracing::debug; | ||
|
||
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] | ||
pub struct SourceRegion { | ||
pub start_line: u32, | ||
pub start_col: u32, | ||
pub end_line: u32, | ||
pub end_col: u32, | ||
} | ||
|
||
impl Debug for SourceRegion { | ||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { | ||
let &Self { start_line, start_col, end_line, end_col } = self; | ||
write!(fmt, "{start_line}:{start_col} - {end_line}:{end_col}") | ||
} | ||
} | ||
|
||
fn ensure_non_empty_span( | ||
source_map: &SourceMap, | ||
fn_cov_info: &FunctionCoverageInfo, | ||
span: Span, | ||
) -> Option<Span> { | ||
if !span.is_empty() { | ||
return Some(span); | ||
} | ||
let lo = span.lo(); | ||
let hi = span.hi(); | ||
// The span is empty, so try to expand it to cover an adjacent '{' or '}', | ||
// but only within the bounds of the body span. | ||
let try_next = hi < fn_cov_info.body_span.hi(); | ||
let try_prev = fn_cov_info.body_span.lo() < lo; | ||
if !(try_next || try_prev) { | ||
return None; | ||
} | ||
source_map | ||
.span_to_source(span, |src, start, end| try { | ||
// Adjusting span endpoints by `BytePos(1)` is normally a bug, | ||
// but in this case we have specifically checked that the character | ||
// we're skipping over is one of two specific ASCII characters, so | ||
// adjusting by exactly 1 byte is correct. | ||
if try_next && src.as_bytes()[end] == b'{' { | ||
Some(span.with_hi(hi + BytePos(1))) | ||
} else if try_prev && src.as_bytes()[start - 1] == b'}' { | ||
Some(span.with_lo(lo - BytePos(1))) | ||
} else { | ||
None | ||
} | ||
}) | ||
.ok()? | ||
} | ||
|
||
/// If `llvm-cov` sees a source region that is improperly ordered (end < start), | ||
/// it will immediately exit with a fatal error. To prevent that from happening, | ||
/// discard regions that are improperly ordered, or might be interpreted in a | ||
/// way that makes them improperly ordered. | ||
fn check_source_region(source_region: SourceRegion) -> Option<SourceRegion> { | ||
let SourceRegion { start_line, start_col, end_line, end_col } = source_region; | ||
// Line/column coordinates are supposed to be 1-based. If we ever emit | ||
// coordinates of 0, `llvm-cov` might misinterpret them. | ||
let all_nonzero = [start_line, start_col, end_line, end_col].into_iter().all(|x| x != 0); | ||
// Coverage mappings use the high bit of `end_col` to indicate that a | ||
// region is actually a "gap" region, so make sure it's unset. | ||
let end_col_has_high_bit_unset = (end_col & (1 << 31)) == 0; | ||
// If a region is improperly ordered (end < start), `llvm-cov` will exit | ||
// with a fatal error, which is inconvenient for users and hard to debug. | ||
let is_ordered = (start_line, start_col) <= (end_line, end_col); | ||
if all_nonzero && end_col_has_high_bit_unset && is_ordered { | ||
Some(source_region) | ||
} else { | ||
debug!( | ||
?source_region, | ||
?all_nonzero, | ||
?end_col_has_high_bit_unset, | ||
?is_ordered, | ||
"Skipping source region that would be misinterpreted or rejected by LLVM" | ||
); | ||
// If this happens in a debug build, ICE to make it easier to notice. | ||
debug_assert!(false, "Improper source region: {source_region:?}"); | ||
None | ||
} | ||
} | ||
|
||
/// Converts the span into its start line and column, and end line and column. | ||
/// | ||
/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by | ||
/// the compiler, these column numbers are denoted in **bytes**, because that's what | ||
/// LLVM's `llvm-cov` tool expects to see in coverage maps. | ||
/// | ||
/// Returns `None` if the conversion failed for some reason. This shouldn't happen, | ||
/// but it's hard to rule out entirely (especially in the presence of complex macros | ||
/// or other expansions), and if it does happen then skipping a span or function is | ||
/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. | ||
pub(crate) fn make_source_region( | ||
source_map: &SourceMap, | ||
fn_cov_info: &FunctionCoverageInfo, | ||
file: &SourceFile, | ||
span: Span, | ||
) -> Option<SourceRegion> { | ||
let span = ensure_non_empty_span(source_map, fn_cov_info, span)?; | ||
let lo = span.lo(); | ||
let hi = span.hi(); | ||
// Column numbers need to be in bytes, so we can't use the more convenient | ||
// `SourceMap` methods for looking up file coordinates. | ||
let line_and_byte_column = |pos: BytePos| -> Option<(usize, usize)> { | ||
let rpos = file.relative_position(pos); | ||
let line_index = file.lookup_line(rpos)?; | ||
let line_start = file.lines()[line_index]; | ||
// Line numbers and column numbers are 1-based, so add 1 to each. | ||
Some((line_index + 1, ((rpos - line_start).0 as usize) + 1)) | ||
}; | ||
let (mut start_line, start_col) = line_and_byte_column(lo)?; | ||
let (mut end_line, end_col) = line_and_byte_column(hi)?; | ||
// Apply an offset so that code in doctests has correct line numbers. | ||
// FIXME(#79417): Currently we have no way to offset doctest _columns_. | ||
start_line = source_map.doctest_offset_line(&file.name, start_line); | ||
end_line = source_map.doctest_offset_line(&file.name, end_line); | ||
check_source_region(SourceRegion { | ||
start_line: start_line as u32, | ||
start_col: start_col as u32, | ||
end_line: end_line as u32, | ||
end_col: end_col as u32, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.