Auto merge of #132863 - matthiaskrgr:rollup-1zbjz5f, r=matthiaskrgr

Rollup of 3 pull requests

Successful merges:

 - #132675 (coverage: Restrict empty-span expansion to only cover `{` and `}`)
 - #132849 (Miri subtree update)
 - #132858 (Update minifer version to `0.3.2`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-11-10 18:06:48 +00:00
commit 2128d8df0e
111 changed files with 1281 additions and 684 deletions

View File

@ -2223,12 +2223,9 @@ dependencies = [
[[package]]
name = "minifier"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa3f302fe0f8de065d4a2d1ed64f60204623cac58b80cd3c2a83a25d5a7d437"
dependencies = [
"clap",
]
checksum = "bd559bbf5d350ac7f2c1cf92ed71a869b847a92bce0c1318b47932a5b5f65cdd"
[[package]]
name = "minimal-lexical"

View File

@ -1,5 +1,7 @@
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion};
use crate::coverageinfo::mapgen::LocalFileId;
/// Must match the layout of `LLVMRustCounterKind`.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
@ -137,8 +139,12 @@ pub(crate) struct CoverageSpan {
}
impl CoverageSpan {
pub(crate) fn from_source_region(file_id: u32, code_region: &SourceRegion) -> Self {
let &SourceRegion { file_name: _, start_line, start_col, end_line, end_col } = code_region;
pub(crate) fn from_source_region(
local_file_id: LocalFileId,
code_region: &SourceRegion,
) -> Self {
let file_id = local_file_id.as_u32();
let &SourceRegion { start_line, start_col, end_line, end_col } = code_region;
// Internally, LLVM uses the high bit of `end_col` to distinguish between
// code regions and gap regions, so it can't be used by the column number.
assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}");

View File

@ -6,7 +6,6 @@ use rustc_middle::mir::coverage::{
SourceRegion,
};
use rustc_middle::ty::Instance;
use rustc_span::Symbol;
use tracing::{debug, instrument};
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
@ -180,7 +179,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
}
pub(crate) struct FunctionCoverage<'tcx> {
function_coverage_info: &'tcx FunctionCoverageInfo,
pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo,
is_used: bool,
counters_seen: BitSet<CounterId>,
@ -199,11 +198,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
}
/// Returns an iterator over all filenames used by this function's mappings.
pub(crate) fn all_file_names(&self) -> impl Iterator<Item = Symbol> + Captures<'_> {
self.function_coverage_info.mappings.iter().map(|mapping| mapping.source_region.file_name)
}
/// Convert this function's coverage expression data into a form that can be
/// passed through FFI to LLVM.
pub(crate) fn counter_expressions(

View File

@ -12,8 +12,10 @@ use rustc_index::IndexVec;
use rustc_middle::mir::coverage::MappingKind;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_span::Symbol;
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::def_id::DefIdSet;
use rustc_span::{Span, Symbol};
use rustc_target::spec::HasTargetSpec;
use tracing::debug;
@ -70,8 +72,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
.collect::<Vec<_>>();
let all_file_names =
function_coverage_entries.iter().flat_map(|(_, fn_cov)| fn_cov.all_file_names());
let all_file_names = function_coverage_entries
.iter()
.map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span)
.map(|span| span_file_name(tcx, span));
let global_file_table = GlobalFileTable::new(all_file_names);
// Encode all filenames referenced by coverage mappings in this CGU.
@ -96,7 +100,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
let is_used = function_coverage.is_used();
let coverage_mapping_buffer =
encode_mappings_for_function(&global_file_table, &function_coverage);
encode_mappings_for_function(tcx, &global_file_table, &function_coverage);
if coverage_mapping_buffer.is_empty() {
if function_coverage.is_used() {
@ -164,13 +168,13 @@ impl GlobalFileTable {
Self { raw_file_table }
}
fn global_file_id_for_file_name(&self, file_name: Symbol) -> u32 {
fn global_file_id_for_file_name(&self, file_name: Symbol) -> GlobalFileId {
let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| {
bug!("file name not found in prepared global file table: {file_name}");
});
// The raw file table doesn't include an entry for the working dir
// (which has ID 0), so add 1 to get the correct ID.
(raw_id + 1) as u32
GlobalFileId::from_usize(raw_id + 1)
}
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
@ -196,19 +200,27 @@ impl GlobalFileTable {
}
rustc_index::newtype_index! {
struct LocalFileId {}
/// An index into the CGU's overall list of file paths. The underlying paths
/// will be embedded in the `__llvm_covmap` linker section.
struct GlobalFileId {}
}
rustc_index::newtype_index! {
/// An index into a function's list of global file IDs. That underlying list
/// of local-to-global mappings will be embedded in the function's record in
/// the `__llvm_covfun` linker section.
pub(crate) struct LocalFileId {}
}
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
/// file IDs.
#[derive(Default)]
struct VirtualFileMapping {
local_to_global: IndexVec<LocalFileId, u32>,
global_to_local: FxIndexMap<u32, LocalFileId>,
local_to_global: IndexVec<LocalFileId, GlobalFileId>,
global_to_local: FxIndexMap<GlobalFileId, LocalFileId>,
}
impl VirtualFileMapping {
fn local_id_for_global(&mut self, global_file_id: u32) -> LocalFileId {
fn local_id_for_global(&mut self, global_file_id: GlobalFileId) -> LocalFileId {
*self
.global_to_local
.entry(global_file_id)
@ -216,16 +228,26 @@ impl VirtualFileMapping {
}
fn into_vec(self) -> Vec<u32> {
self.local_to_global.raw
// This conversion should be optimized away to ~zero overhead.
// In any case, it's probably not hot enough to worry about.
self.local_to_global.into_iter().map(|global| global.as_u32()).collect()
}
}
fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
let source_file = tcx.sess.source_map().lookup_source_file(span.lo());
let name =
source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy();
Symbol::intern(&name)
}
/// Using the expressions and counter regions collected for a single function,
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
/// entry. The payload is returned as a vector of bytes.
///
/// Newly-encountered filenames will be added to the global file table.
fn encode_mappings_for_function(
tcx: TyCtxt<'_>,
global_file_table: &GlobalFileTable,
function_coverage: &FunctionCoverage<'_>,
) -> Vec<u8> {
@ -242,53 +264,45 @@ fn encode_mappings_for_function(
let mut mcdc_branch_regions = vec![];
let mut mcdc_decision_regions = vec![];
// Group mappings into runs with the same filename, preserving the order
// yielded by `FunctionCoverage`.
// Prepare file IDs for each filename, and prepare the mapping data so that
// we can pass it through FFI to LLVM.
for (file_name, counter_regions_for_file) in
&counter_regions.group_by(|(_, region)| region.file_name)
{
// Look up the global file ID for this filename.
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
// Currently a function's mappings must all be in the same file as its body span.
let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);
// Associate that global file ID with a local file ID for this function.
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
debug!(" file id: {local_file_id:?} => global {global_file_id} = '{file_name:?}'");
// Look up the global file ID for that filename.
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
// For each counter/region pair in this function+file, convert it to a
// form suitable for FFI.
for (mapping_kind, region) in counter_regions_for_file {
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
let span = ffi::CoverageSpan::from_source_region(local_file_id.as_u32(), region);
match mapping_kind {
MappingKind::Code(term) => {
code_regions
.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
}
MappingKind::Branch { true_term, false_term } => {
branch_regions.push(ffi::BranchRegion {
span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
});
}
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
});
}
MappingKind::MCDCDecision(mcdc_decision_params) => {
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
span,
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(
mcdc_decision_params,
),
});
}
// Associate that global file ID with a local file ID for this function.
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
// For each counter/region pair in this function+file, convert it to a
// form suitable for FFI.
for (mapping_kind, region) in counter_regions {
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
let span = ffi::CoverageSpan::from_source_region(local_file_id, region);
match mapping_kind {
MappingKind::Code(term) => {
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
}
MappingKind::Branch { true_term, false_term } => {
branch_regions.push(ffi::BranchRegion {
span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
});
}
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
});
}
MappingKind::MCDCDecision(mcdc_decision_params) => {
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
span,
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
});
}
}
}

View File

@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter};
use rustc_index::IndexVec;
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_span::{Span, Symbol};
use rustc_span::Span;
rustc_index::newtype_index! {
/// Used by [`CoverageKind::BlockMarker`] to mark blocks during THIR-to-MIR
@ -158,7 +158,6 @@ impl Debug for CoverageKind {
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, Eq, PartialOrd, Ord)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct SourceRegion {
pub file_name: Symbol,
pub start_line: u32,
pub start_col: u32,
pub end_line: u32,
@ -167,11 +166,8 @@ pub struct SourceRegion {
impl Debug for SourceRegion {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
write!(
fmt,
"{}:{}:{} - {}:{}",
self.file_name, self.start_line, self.start_col, self.end_line, self.end_col
)
let &Self { start_line, start_col, end_line, end_col } = self;
write!(fmt, "{start_line}:{start_col} - {end_line}:{end_col}")
}
}
@ -246,6 +242,7 @@ pub struct Mapping {
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct FunctionCoverageInfo {
pub function_source_hash: u64,
pub body_span: Span,
pub num_counters: usize,
pub mcdc_bitmap_bits: usize,
pub expressions: IndexVec<ExpressionId, Expression>,

View File

@ -596,8 +596,10 @@ fn write_function_coverage_info(
function_coverage_info: &coverage::FunctionCoverageInfo,
w: &mut dyn io::Write,
) -> io::Result<()> {
let coverage::FunctionCoverageInfo { expressions, mappings, .. } = function_coverage_info;
let coverage::FunctionCoverageInfo { body_span, expressions, mappings, .. } =
function_coverage_info;
writeln!(w, "{INDENT}coverage body span: {body_span:?}")?;
for (id, expression) in expressions.iter_enumerated() {
writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?;
}

View File

@ -22,8 +22,8 @@ use rustc_middle::mir::{
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, Pos, RelativeBytePos, Span, Symbol};
use tracing::{debug, debug_span, instrument, trace};
use rustc_span::{BytePos, Pos, SourceFile, Span};
use tracing::{debug, debug_span, trace};
use crate::coverage::counters::{CounterIncrementSite, CoverageCounters};
use crate::coverage::graph::CoverageGraph;
@ -122,6 +122,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
function_source_hash: hir_info.function_source_hash,
body_span: hir_info.body_span,
num_counters: coverage_counters.num_counters(),
mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits,
expressions: coverage_counters.into_expressions(),
@ -142,19 +143,11 @@ fn create_mappings<'tcx>(
coverage_counters: &CoverageCounters,
) -> Vec<Mapping> {
let source_map = tcx.sess.source_map();
let body_span = hir_info.body_span;
let source_file = source_map.lookup_source_file(body_span.lo());
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
let file_name = Symbol::intern(
&source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
);
let file = source_map.lookup_source_file(hir_info.body_span.lo());
let term_for_bcb =
|bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters");
let region_for_span = |span: Span| make_source_region(source_map, file_name, span, body_span);
let region_for_span = |span: Span| make_source_region(source_map, hir_info, &file, span);
// Fully destructure the mappings struct to make sure we don't miss any kinds.
let ExtractedMappings {
@ -398,7 +391,42 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb
data.statements.insert(0, statement);
}
/// Convert the Span into its file name, start line and column, and end line and column.
fn ensure_non_empty_span(
source_map: &SourceMap,
hir_info: &ExtractedHirInfo,
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 < hir_info.body_span.hi();
let try_prev = hir_info.body_span.lo() < lo;
if !(try_next || try_prev) {
return None;
}
source_map
.span_to_source(span, |src, start, end| try {
// We're only checking for specific ASCII characters, so we don't
// have to worry about multi-byte code points.
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()?
}
/// 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
@ -408,56 +436,29 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb
/// 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.
#[instrument(level = "debug", skip(source_map))]
fn make_source_region(
source_map: &SourceMap,
file_name: Symbol,
hir_info: &ExtractedHirInfo,
file: &SourceFile,
span: Span,
body_span: Span,
) -> Option<SourceRegion> {
let span = ensure_non_empty_span(source_map, hir_info, span)?;
let lo = span.lo();
let hi = span.hi();
let file = source_map.lookup_source_file(lo);
if !file.contains(hi) {
debug!(?span, ?file, ?lo, ?hi, "span crosses multiple files; skipping");
return None;
}
// Column numbers need to be in bytes, so we can't use the more convenient
// `SourceMap` methods for looking up file coordinates.
let rpos_and_line_and_byte_column = |pos: BytePos| -> Option<(RelativeBytePos, usize, usize)> {
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((rpos, line_index + 1, (rpos - line_start).to_usize() + 1))
Some((line_index + 1, (rpos - line_start).to_usize() + 1))
};
let (lo_rpos, mut start_line, mut start_col) = rpos_and_line_and_byte_column(lo)?;
let (hi_rpos, mut end_line, mut end_col) = rpos_and_line_and_byte_column(hi)?;
// If the span is empty, try to expand it horizontally by one character's
// worth of bytes, so that it is more visible in `llvm-cov` reports.
// We do this after resolving line/column numbers, so that empty spans at the
// end of a line get an extra column instead of wrapping to the next line.
if span.is_empty()
&& body_span.contains(span)
&& let Some(src) = &file.src
{
// Prefer to expand the end position, if it won't go outside the body span.
if hi < body_span.hi() {
let hi_rpos = hi_rpos.to_usize();
let nudge_bytes = src.ceil_char_boundary(hi_rpos + 1) - hi_rpos;
end_col += nudge_bytes;
} else if lo > body_span.lo() {
let lo_rpos = lo_rpos.to_usize();
let nudge_bytes = lo_rpos - src.floor_char_boundary(lo_rpos - 1);
// Subtract the nudge, but don't go below column 1.
start_col = start_col.saturating_sub(nudge_bytes).max(1);
}
// If neither nudge could be applied, stick with the empty span coordinates.
}
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_.
@ -465,7 +466,6 @@ fn make_source_region(
end_line = source_map.doctest_offset_line(&file.name, end_line);
check_source_region(SourceRegion {
file_name,
start_line: start_line as u32,
start_col: start_col as u32,
end_line: end_line as u32,
@ -478,7 +478,7 @@ fn make_source_region(
/// 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 { file_name: _, start_line, start_col, end_line, end_col } = source_region;
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.

View File

@ -9,7 +9,6 @@
#![feature(let_chains)]
#![feature(map_try_insert)]
#![feature(never_type)]
#![feature(round_char_boundary)]
#![feature(try_blocks)]
#![feature(yeet_expr)]
#![warn(unreachable_pub)]

View File

@ -534,7 +534,7 @@ impl SourceMap {
/// Extracts the source surrounding the given `Span` using the `extract_source` function. The
/// extract function takes three arguments: a string slice containing the source, an index in
/// the slice for the beginning of the span and an index in the slice for the end of the span.
fn span_to_source<F, T>(&self, sp: Span, extract_source: F) -> Result<T, SpanSnippetError>
pub fn span_to_source<F, T>(&self, sp: Span, extract_source: F) -> Result<T, SpanSnippetError>
where
F: Fn(&str, usize, usize) -> Result<T, SpanSnippetError>,
{

View File

@ -13,7 +13,7 @@ rinja = { version = "0.3", default-features = false, features = ["config"] }
base64 = "0.21.7"
itertools = "0.12"
indexmap = "2"
minifier = "0.3.1"
minifier = { version = "0.3.2", default-features = false }
pulldown-cmark-old = { version = "0.9.6", package = "pulldown-cmark", default-features = false }
regex = "1"
rustdoc-json-types = { path = "../rustdoc-json-types" }

View File

@ -58,11 +58,20 @@ jobs:
- name: rustdoc
run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items
coverage:
name: coverage report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/workflows/setup
- name: coverage
run: ./miri test --coverage
# Summary job for the merge queue.
# ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
# And they should be added below in `cron-fail-notify` as well.
conclusion:
needs: [build, style]
needs: [build, style, coverage]
# We need to ensure this job does *not* get skipped if its dependencies fail,
# because a skipped job is considered a success by GitHub. So we have to
# overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
@ -86,7 +95,7 @@ jobs:
contents: write
# ... and create a PR.
pull-requests: write
needs: [build, style]
needs: [build, style, coverage]
if: ${{ github.event_name == 'schedule' && failure() }}
steps:
# Send a Zulip notification

View File

@ -13,6 +13,25 @@ for a list of Miri maintainers.
[Rust Zulip]: https://rust-lang.zulipchat.com
### Pull review process
When you get a review, please take care of the requested changes in new commits. Do not amend
existing commits. Generally avoid force-pushing. The only time you should force push is when there
is a conflict with the master branch (in that case you should rebase across master, not merge), and
all the way at the end of the review process when the reviewer tells you that the PR is done and you
should squash the commits. For the latter case, use `git rebase --keep-base ...` to squash without
changing the base commit your PR branches off of. Use your own judgment and the reviewer's guidance
to decide whether the PR should be squashed into a single commit or multiple logically separate
commits. (All this is to work around the fact that Github is quite bad at dealing with force pushes
and does not support `git range-diff`. Maybe one day Github will be good at git and then life can
become easier.)
Most PRs bounce back and forth between the reviewer and the author several times, so it is good to
keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and
`S-waiting-for-author` labels for that. If a reviewer asked you to do some changes and you think
they are all taken care of, post a comment saying `@rustbot ready` to mark a PR as ready for the
next round of review.
### Larger-scale contributions
If you are thinking about making a larger-scale contribution -- in particular anything that needs
@ -45,14 +64,6 @@ process for such contributions:
This process is largely informal, and its primary goal is to more clearly communicate expectations.
Please get in touch with us if you have any questions!
### Managing the review state
Most PRs bounce back and forth between the reviewer and the author several times, so it is good to
keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and
`S-waiting-for-author` labels for that. If a reviewer asked you to do some changes and you think
they are all taken care of, post a comment saying `@rustbot ready` to mark a PR as ready for the
next round of review.
## Preparing the build environment
Miri heavily relies on internal and unstable rustc interfaces to execute MIR,

View File

@ -154,7 +154,7 @@ case $HOST_TARGET in
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap threadname pthread
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random sync threadname pthread
TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std

View File

@ -1 +1 @@
arithmetic-side-effects-allowed = ["rustc_abi::Size"]
arithmetic-side-effects-allowed = ["rustc_abi::Size", "rustc_apfloat::ieee::IeeeFloat"]

View File

@ -63,6 +63,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "getrandom"
version = "0.2.12"
@ -100,9 +106,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "libc"
version = "0.2.153"
version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "libredox"
@ -138,11 +144,18 @@ dependencies = [
"rustc_version",
"serde_json",
"shell-words",
"tempfile",
"walkdir",
"which",
"xshell",
]
[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "option-ext"
version = "0.2.0"
@ -195,9 +208,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.34"
version = "0.38.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
dependencies = [
"bitflags",
"errno",
@ -276,6 +289,19 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
dependencies = [
"cfg-if",
"fastrand",
"once_cell",
"rustix",
"windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
version = "1.0.57"
@ -357,6 +383,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"

View File

@ -24,3 +24,4 @@ rustc_version = "0.4"
dunce = "1.0.4"
directories = "5"
serde_json = "1"
tempfile = "3.13.0"

View File

@ -172,7 +172,8 @@ impl Command {
Command::Install { flags } => Self::install(flags),
Command::Build { flags } => Self::build(flags),
Command::Check { flags } => Self::check(flags),
Command::Test { bless, flags, target } => Self::test(bless, flags, target),
Command::Test { bless, flags, target, coverage } =>
Self::test(bless, flags, target, coverage),
Command::Run { dep, verbose, many_seeds, target, edition, flags } =>
Self::run(dep, verbose, many_seeds, target, edition, flags),
Command::Doc { flags } => Self::doc(flags),
@ -458,9 +459,20 @@ impl Command {
Ok(())
}
fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
fn test(
bless: bool,
mut flags: Vec<String>,
target: Option<String>,
coverage: bool,
) -> Result<()> {
let mut e = MiriEnv::new()?;
let coverage = coverage.then_some(crate::coverage::CoverageReport::new()?);
if let Some(report) = &coverage {
report.add_env_vars(&mut e)?;
}
// Prepare a sysroot. (Also builds cargo-miri, which we need.)
e.build_miri_sysroot(/* quiet */ false, target.as_deref())?;
@ -479,6 +491,11 @@ impl Command {
// Then test, and let caller control flags.
// Only in root project as `cargo-miri` has no tests.
e.test(".", &flags)?;
if let Some(coverage) = &coverage {
coverage.show_coverage_report(&e)?;
}
Ok(())
}

View File

@ -0,0 +1,91 @@
use std::path::PathBuf;
use anyhow::{Context, Result};
use path_macro::path;
use tempfile::TempDir;
use xshell::cmd;
use crate::util::MiriEnv;
/// CoverageReport can generate code coverage reports for miri.
pub struct CoverageReport {
/// path is a temporary directory where intermediate coverage artifacts will be stored.
/// (The final output will be stored in a permanent location.)
path: TempDir,
}
impl CoverageReport {
/// Creates a new CoverageReport.
///
/// # Errors
///
/// An error will be returned if a temporary directory could not be created.
pub fn new() -> Result<Self> {
Ok(Self { path: TempDir::new()? })
}
/// add_env_vars will add the required environment variables to MiriEnv `e`.
pub fn add_env_vars(&self, e: &mut MiriEnv) -> Result<()> {
let mut rustflags = e.sh.var("RUSTFLAGS")?;
rustflags.push_str(" -C instrument-coverage");
e.sh.set_var("RUSTFLAGS", rustflags);
// Copy-pasting from: https://doc.rust-lang.org/rustc/instrument-coverage.html#instrumentation-based-code-coverage
// The format symbols below have the following meaning:
// - %p - The process ID.
// - %Nm - the instrumented binarys signature:
// The runtime creates a pool of N raw profiles, used for on-line
// profile merging. The runtime takes care of selecting a raw profile
// from the pool, locking it, and updating it before the program
// exits. N must be between 1 and 9, and defaults to 1 if omitted
// (with simply %m).
//
// Additionally the default for LLVM_PROFILE_FILE is default_%m_%p.profraw.
// So we just use the same template, replacing "default" with "miri".
let file_template = self.path.path().join("miri_%m_%p.profraw");
e.sh.set_var("LLVM_PROFILE_FILE", file_template);
Ok(())
}
/// show_coverage_report will print coverage information using the artifact
/// files in `self.path`.
pub fn show_coverage_report(&self, e: &MiriEnv) -> Result<()> {
let profraw_files = self.profraw_files()?;
let profdata_bin = path!(e.libdir / ".." / "bin" / "llvm-profdata");
let merged_file = path!(e.miri_dir / "target" / "coverage.profdata");
// Merge the profraw files
cmd!(e.sh, "{profdata_bin} merge -sparse {profraw_files...} -o {merged_file}")
.quiet()
.run()?;
// Create the coverage report.
let cov_bin = path!(e.libdir / ".." / "bin" / "llvm-cov");
let miri_bin =
e.build_get_binary(".").context("failed to get filename of miri executable")?;
cmd!(
e.sh,
"{cov_bin} report --instr-profile={merged_file} --object {miri_bin} --sources src/"
)
.run()?;
println!("Profile data saved in {}", merged_file.display());
Ok(())
}
/// profraw_files returns the profraw files in `self.path`.
///
/// # Errors
///
/// An error will be returned if `self.path` can't be read.
fn profraw_files(&self) -> Result<Vec<PathBuf>> {
Ok(std::fs::read_dir(&self.path)?
.filter_map(|r| r.ok())
.filter(|e| e.file_type().is_ok_and(|t| t.is_file()))
.map(|e| e.path())
.filter(|p| p.extension().is_some_and(|e| e == "profraw"))
.collect())
}
}

View File

@ -2,6 +2,7 @@
mod args;
mod commands;
mod coverage;
mod util;
use std::ops::Range;
@ -34,6 +35,8 @@ pub enum Command {
/// The cross-interpretation target.
/// If none then the host is the target.
target: Option<String>,
/// Produce coverage report if set.
coverage: bool,
/// Flags that are passed through to the test harness.
flags: Vec<String>,
},
@ -158,9 +161,12 @@ fn main() -> Result<()> {
let mut target = None;
let mut bless = false;
let mut flags = Vec::new();
let mut coverage = false;
loop {
if args.get_long_flag("bless")? {
bless = true;
} else if args.get_long_flag("coverage")? {
coverage = true;
} else if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(flag) = args.get_other() {
@ -169,7 +175,7 @@ fn main() -> Result<()> {
break;
}
}
Command::Test { bless, flags, target }
Command::Test { bless, flags, target, coverage }
}
Some("run") => {
let mut dep = false;

View File

@ -41,6 +41,8 @@ pub struct MiriEnv {
pub sysroot: PathBuf,
/// The shell we use.
pub sh: Shell,
/// The library dir in the sysroot.
pub libdir: PathBuf,
}
impl MiriEnv {
@ -96,7 +98,8 @@ impl MiriEnv {
// so that Windows can find the DLLs.
if cfg!(windows) {
let old_path = sh.var("PATH")?;
let new_path = env::join_paths(iter::once(libdir).chain(env::split_paths(&old_path)))?;
let new_path =
env::join_paths(iter::once(libdir.clone()).chain(env::split_paths(&old_path)))?;
sh.set_var("PATH", new_path);
}
@ -111,7 +114,7 @@ impl MiriEnv {
std::process::exit(1);
}
Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags })
Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags, libdir })
}
pub fn cargo_cmd(&self, crate_dir: impl AsRef<OsStr>, cmd: &str) -> Cmd<'_> {

View File

@ -1 +1 @@
814df6e50eaf89b90793e7d9618bb60f1f18377a
668959740f97e7a22ae340742886d330ab63950f

View File

@ -111,8 +111,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Returns the exposed `AllocId` that corresponds to the specified addr,
// or `None` if the addr is out of bounds
fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option<AllocId> {
let ecx = self.eval_context_ref();
let global_state = ecx.machine.alloc_addresses.borrow();
let this = self.eval_context_ref();
let global_state = this.machine.alloc_addresses.borrow();
assert!(global_state.provenance_mode != ProvenanceMode::Strict);
// We always search the allocation to the right of this address. So if the size is structly
@ -134,7 +134,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// entered for addresses that are not the base address, so even zero-sized
// allocations will get recognized at their base address -- but all other
// allocations will *not* be recognized at their "end" address.
let size = ecx.get_alloc_info(alloc_id).size;
let size = this.get_alloc_info(alloc_id).size;
if offset < size.bytes() { Some(alloc_id) } else { None }
}
}?;
@ -142,7 +142,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// We only use this provenance if it has been exposed.
if global_state.exposed.contains(&alloc_id) {
// This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed.
debug_assert!(ecx.is_alloc_live(alloc_id));
debug_assert!(this.is_alloc_live(alloc_id));
Some(alloc_id)
} else {
None
@ -155,9 +155,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
alloc_id: AllocId,
memory_kind: MemoryKind,
) -> InterpResult<'tcx, u64> {
let ecx = self.eval_context_ref();
let mut rng = ecx.machine.rng.borrow_mut();
let info = ecx.get_alloc_info(alloc_id);
let this = self.eval_context_ref();
let mut rng = this.machine.rng.borrow_mut();
let info = this.get_alloc_info(alloc_id);
// This is either called immediately after allocation (and then cached), or when
// adjusting `tcx` pointers (which never get freed). So assert that we are looking
// at a live allocation. This also ensures that we never re-assign an address to an
@ -166,12 +166,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
assert!(!matches!(info.kind, AllocKind::Dead));
// This allocation does not have a base address yet, pick or reuse one.
if ecx.machine.native_lib.is_some() {
if this.machine.native_lib.is_some() {
// In native lib mode, we use the "real" address of the bytes for this allocation.
// This ensures the interpreted program and native code have the same view of memory.
let base_ptr = match info.kind {
AllocKind::LiveData => {
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
if this.tcx.try_get_global_alloc(alloc_id).is_some() {
// For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
let prepared_bytes = MiriAllocBytes::zeroed(info.size, info.align)
.unwrap_or_else(|| {
@ -185,7 +185,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
.unwrap();
ptr
} else {
ecx.get_alloc_bytes_unchecked_raw(alloc_id)?
this.get_alloc_bytes_unchecked_raw(alloc_id)?
}
}
AllocKind::Function | AllocKind::VTable => {
@ -203,11 +203,15 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(base_ptr.expose_provenance().try_into().unwrap());
}
// We are not in native lib mode, so we control the addresses ourselves.
if let Some((reuse_addr, clock)) =
global_state.reuse.take_addr(&mut *rng, info.size, info.align, memory_kind, ecx.active_thread())
{
if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
&mut *rng,
info.size,
info.align,
memory_kind,
this.active_thread(),
) {
if let Some(clock) = clock {
ecx.acquire_clock(&clock);
this.acquire_clock(&clock);
}
interp_ok(reuse_addr)
} else {
@ -230,7 +234,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
.checked_add(max(info.size.bytes(), 1))
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
// Even if `Size` didn't overflow, we might still have filled up the address space.
if global_state.next_base_addr > ecx.target_usize_max() {
if global_state.next_base_addr > this.target_usize_max() {
throw_exhaust!(AddressSpaceFull);
}
@ -243,8 +247,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
alloc_id: AllocId,
memory_kind: MemoryKind,
) -> InterpResult<'tcx, u64> {
let ecx = self.eval_context_ref();
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
let this = self.eval_context_ref();
let mut global_state = this.machine.alloc_addresses.borrow_mut();
let global_state = &mut *global_state;
match global_state.base_addr.get(&alloc_id) {
@ -283,22 +287,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn expose_ptr(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
let ecx = self.eval_context_mut();
let global_state = ecx.machine.alloc_addresses.get_mut();
let this = self.eval_context_mut();
let global_state = this.machine.alloc_addresses.get_mut();
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
if global_state.provenance_mode == ProvenanceMode::Strict {
return interp_ok(());
}
// Exposing a dead alloc is a no-op, because it's not possible to get a dead allocation
// via int2ptr.
if !ecx.is_alloc_live(alloc_id) {
if !this.is_alloc_live(alloc_id) {
return interp_ok(());
}
trace!("Exposing allocation id {alloc_id:?}");
let global_state = ecx.machine.alloc_addresses.get_mut();
let global_state = this.machine.alloc_addresses.get_mut();
global_state.exposed.insert(alloc_id);
if ecx.machine.borrow_tracker.is_some() {
ecx.expose_tag(alloc_id, tag)?;
if this.machine.borrow_tracker.is_some() {
this.expose_tag(alloc_id, tag)?;
}
interp_ok(())
}
@ -306,8 +310,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer> {
trace!("Casting {:#x} to a pointer", addr);
let ecx = self.eval_context_ref();
let global_state = ecx.machine.alloc_addresses.borrow();
let this = self.eval_context_ref();
let global_state = this.machine.alloc_addresses.borrow();
// Potentially emit a warning.
match global_state.provenance_mode {
@ -319,9 +323,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
PAST_WARNINGS.with_borrow_mut(|past_warnings| {
let first = past_warnings.is_empty();
if past_warnings.insert(ecx.cur_span()) {
if past_warnings.insert(this.cur_span()) {
// Newly inserted, so first time we see this span.
ecx.emit_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first });
this.emit_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first });
}
});
}
@ -347,19 +351,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
tag: BorTag,
kind: MemoryKind,
) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
let ecx = self.eval_context_ref();
let this = self.eval_context_ref();
let (prov, offset) = ptr.into_parts(); // offset is relative (AllocId provenance)
let alloc_id = prov.alloc_id();
// Get a pointer to the beginning of this allocation.
let base_addr = ecx.addr_from_alloc_id(alloc_id, kind)?;
let base_addr = this.addr_from_alloc_id(alloc_id, kind)?;
let base_ptr = interpret::Pointer::new(
Provenance::Concrete { alloc_id, tag },
Size::from_bytes(base_addr),
);
// Add offset with the right kind of pointer-overflowing arithmetic.
interp_ok(base_ptr.wrapping_offset(offset, ecx))
interp_ok(base_ptr.wrapping_offset(offset, this))
}
// This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved
@ -371,16 +375,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
bytes: &[u8],
align: Align,
) -> InterpResult<'tcx, MiriAllocBytes> {
let ecx = self.eval_context_ref();
if ecx.machine.native_lib.is_some() {
let this = self.eval_context_ref();
if this.machine.native_lib.is_some() {
// In native lib mode, MiriAllocBytes for global allocations are handled via `prepared_alloc_bytes`.
// This additional call ensures that some `MiriAllocBytes` are always prepared, just in case
// this function gets called before the first time `addr_from_alloc_id` gets called.
ecx.addr_from_alloc_id(id, kind)?;
this.addr_from_alloc_id(id, kind)?;
// The memory we need here will have already been allocated during an earlier call to
// `addr_from_alloc_id` for this allocation. So don't create a new `MiriAllocBytes` here, instead
// fetch the previously prepared bytes from `prepared_alloc_bytes`.
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
let mut global_state = this.machine.alloc_addresses.borrow_mut();
let mut prepared_alloc_bytes = global_state
.prepared_alloc_bytes
.remove(&id)
@ -403,7 +407,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
ptr: interpret::Pointer<Provenance>,
size: i64,
) -> Option<(AllocId, Size)> {
let ecx = self.eval_context_ref();
let this = self.eval_context_ref();
let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance)
@ -411,15 +415,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
alloc_id
} else {
// A wildcard pointer.
ecx.alloc_id_from_addr(addr.bytes(), size)?
this.alloc_id_from_addr(addr.bytes(), size)?
};
// This cannot fail: since we already have a pointer with that provenance, adjust_alloc_root_pointer
// must have been called in the past, so we can just look up the address in the map.
let base_addr = *ecx.machine.alloc_addresses.borrow().base_addr.get(&alloc_id).unwrap();
let base_addr = *this.machine.alloc_addresses.borrow().base_addr.get(&alloc_id).unwrap();
// Wrapping "addr - base_addr"
let rel_offset = ecx.truncate_to_target_usize(addr.bytes().wrapping_sub(base_addr));
let rel_offset = this.truncate_to_target_usize(addr.bytes().wrapping_sub(base_addr));
Some((alloc_id, Size::from_bytes(rel_offset)))
}
}

View File

@ -354,7 +354,7 @@ impl<'tcx> Stack {
self.borrows.get(idx).cloned()
}
#[allow(clippy::len_without_is_empty)] // Stacks are never empty
#[expect(clippy::len_without_is_empty)] // Stacks are never empty
pub fn len(&self) -> usize {
self.borrows.len()
}

View File

@ -1,6 +1,8 @@
use std::cell::RefCell;
use std::collections::VecDeque;
use std::collections::hash_map::Entry;
use std::ops::Not;
use std::rc::Rc;
use std::time::Duration;
use rustc_abi::Size;
@ -121,6 +123,15 @@ struct Futex {
clock: VClock,
}
#[derive(Default, Clone)]
pub struct FutexRef(Rc<RefCell<Futex>>);
impl VisitProvenance for FutexRef {
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
// No provenance in `Futex`.
}
}
/// A thread waiting on a futex.
#[derive(Debug)]
struct FutexWaiter {
@ -137,9 +148,6 @@ pub struct SynchronizationObjects {
rwlocks: IndexVec<RwLockId, RwLock>,
condvars: IndexVec<CondvarId, Condvar>,
pub(super) init_onces: IndexVec<InitOnceId, InitOnce>,
/// Futex info for the futex at the given address.
futexes: FxHashMap<u64, Futex>,
}
// Private extension trait for local helper methods
@ -184,7 +192,7 @@ impl SynchronizationObjects {
}
impl<'tcx> AllocExtra<'tcx> {
pub fn get_sync<T: 'static>(&self, offset: Size) -> Option<&T> {
fn get_sync<T: 'static>(&self, offset: Size) -> Option<&T> {
self.sync.get(&offset).and_then(|s| s.downcast_ref::<T>())
}
}
@ -273,27 +281,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Get the synchronization primitive associated with the given pointer,
/// or initialize a new one.
///
/// Return `None` if this pointer does not point to at least 1 byte of mutable memory.
fn get_sync_or_init<'a, T: 'static>(
&'a mut self,
ptr: Pointer,
new: impl FnOnce(&'a mut MiriMachine<'tcx>) -> InterpResult<'tcx, T>,
) -> InterpResult<'tcx, &'a T>
new: impl FnOnce(&'a mut MiriMachine<'tcx>) -> T,
) -> Option<&'a T>
where
'tcx: 'a,
{
let this = self.eval_context_mut();
// Ensure there is memory behind this pointer, so that this allocation
// is truly the only place where the data could be stored.
this.check_ptr_access(ptr, Size::from_bytes(1), CheckInAllocMsg::InboundsTest)?;
let (alloc, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc)?;
if !this.ptr_try_get_alloc_id(ptr, 0).ok().is_some_and(|(alloc_id, offset, ..)| {
let info = this.get_alloc_info(alloc_id);
info.kind == AllocKind::LiveData && info.mutbl.is_mut() && offset < info.size
}) {
return None;
}
// This cannot fail now.
let (alloc, offset, _) = this.ptr_get_alloc_id(ptr, 0).unwrap();
let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc).unwrap();
// Due to borrow checker reasons, we have to do the lookup twice.
if alloc_extra.get_sync::<T>(offset).is_none() {
let new = new(machine)?;
let new = new(machine);
alloc_extra.sync.insert(offset, Box::new(new));
}
interp_ok(alloc_extra.get_sync::<T>(offset).unwrap())
Some(alloc_extra.get_sync::<T>(offset).unwrap())
}
#[inline]
@ -690,33 +703,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error.
fn futex_wait(
&mut self,
addr: u64,
futex_ref: FutexRef,
bitset: u32,
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
errno_timeout: Scalar,
errno_timeout: IoError,
) {
let this = self.eval_context_mut();
let thread = this.active_thread();
let futex = &mut this.machine.sync.futexes.entry(addr).or_default();
let mut futex = futex_ref.0.borrow_mut();
let waiters = &mut futex.waiters;
assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
waiters.push_back(FutexWaiter { thread, bitset });
drop(futex);
this.block_thread(
BlockReason::Futex { addr },
BlockReason::Futex,
timeout,
callback!(
@capture<'tcx> {
addr: u64,
futex_ref: FutexRef,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
errno_timeout: Scalar,
errno_timeout: IoError,
}
@unblock = |this| {
let futex = this.machine.sync.futexes.get(&addr).unwrap();
let futex = futex_ref.0.borrow();
// Acquire the clock of the futex.
if let Some(data_race) = &this.machine.data_race {
data_race.acquire_clock(&futex.clock, &this.machine.threads);
@ -728,7 +743,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
@timeout = |this| {
// Remove the waiter from the futex.
let thread = this.active_thread();
let futex = this.machine.sync.futexes.get_mut(&addr).unwrap();
let mut futex = futex_ref.0.borrow_mut();
futex.waiters.retain(|waiter| waiter.thread != thread);
// Set errno and write return value.
this.set_last_error(errno_timeout)?;
@ -739,12 +754,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
);
}
/// Wake up the first thread in the queue that matches any of the bits in the bitset.
/// Returns whether anything was woken.
fn futex_wake(&mut self, addr: u64, bitset: u32) -> InterpResult<'tcx, bool> {
fn futex_wake(&mut self, futex_ref: &FutexRef, bitset: u32) -> InterpResult<'tcx, bool> {
let this = self.eval_context_mut();
let Some(futex) = this.machine.sync.futexes.get_mut(&addr) else {
return interp_ok(false);
};
let mut futex = futex_ref.0.borrow_mut();
let data_race = &this.machine.data_race;
// Each futex-wake happens-before the end of the futex wait
@ -757,7 +771,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(false);
};
let waiter = futex.waiters.remove(i).unwrap();
this.unblock_thread(waiter.thread, BlockReason::Futex { addr })?;
drop(futex);
this.unblock_thread(waiter.thread, BlockReason::Futex)?;
interp_ok(true)
}
}

View File

@ -1,7 +1,6 @@
//! Implements threads.
use std::mem;
use std::num::TryFromIntError;
use std::sync::atomic::Ordering::Relaxed;
use std::task::Poll;
use std::time::{Duration, SystemTime};
@ -127,26 +126,6 @@ impl Idx for ThreadId {
}
}
impl TryFrom<u64> for ThreadId {
type Error = TryFromIntError;
fn try_from(id: u64) -> Result<Self, Self::Error> {
u32::try_from(id).map(Self)
}
}
impl TryFrom<i128> for ThreadId {
type Error = TryFromIntError;
fn try_from(id: i128) -> Result<Self, Self::Error> {
u32::try_from(id).map(Self)
}
}
impl From<u32> for ThreadId {
fn from(id: u32) -> Self {
Self(id)
}
}
impl From<ThreadId> for u64 {
fn from(t: ThreadId) -> Self {
t.0.into()
@ -168,7 +147,7 @@ pub enum BlockReason {
/// Blocked on a reader-writer lock.
RwLock(RwLockId),
/// Blocked on a Futex variable.
Futex { addr: u64 },
Futex,
/// Blocked on an InitOnce.
InitOnce(InitOnceId),
/// Blocked on epoll.
@ -448,6 +427,10 @@ pub enum TimeoutAnchor {
Absolute,
}
/// An error signaling that the requested thread doesn't exist.
#[derive(Debug, Copy, Clone)]
pub struct ThreadNotFound;
/// A set of threads.
#[derive(Debug)]
pub struct ThreadManager<'tcx> {
@ -509,6 +492,16 @@ impl<'tcx> ThreadManager<'tcx> {
}
}
pub fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadNotFound> {
if let Ok(id) = id.try_into()
&& usize::try_from(id).is_ok_and(|id| id < self.threads.len())
{
Ok(ThreadId(id))
} else {
Err(ThreadNotFound)
}
}
/// Check if we have an allocation for the given thread local static for the
/// active thread.
fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<StrictPointer> {
@ -534,6 +527,7 @@ impl<'tcx> ThreadManager<'tcx> {
) -> &mut Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
&mut self.threads[self.active_thread].stack
}
pub fn all_stacks(
&self,
) -> impl Iterator<Item = (ThreadId, &[Frame<'tcx, Provenance, FrameExtra<'tcx>>])> {
@ -868,6 +862,11 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
// Public interface to thread management.
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline]
fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadNotFound> {
self.eval_context_ref().machine.threads.thread_id_try_from(id)
}
/// Get a thread-specific allocation id for the given thread-local static.
/// If needed, allocate a new one.
fn get_or_create_thread_local_alloc(
@ -1160,8 +1159,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Set the name of the current thread. The buffer must not include the null terminator.
#[inline]
fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
let this = self.eval_context_mut();
this.machine.threads.set_thread_name(thread, new_thread_name);
self.eval_context_mut().machine.threads.set_thread_name(thread, new_thread_name);
}
#[inline]

View File

@ -300,7 +300,6 @@ impl<'tcx> StoreBuffer {
interp_ok(())
}
#[allow(clippy::if_same_then_else, clippy::needless_bool)]
/// Selects a valid store element in the buffer.
fn fetch_store<R: rand::Rng + ?Sized>(
&self,

View File

@ -423,7 +423,7 @@ pub fn create_ecx<'tcx>(
/// Evaluates the entry function specified by `entry_id`.
/// Returns `Some(return_code)` if program executed completed.
/// Returns `None` if an evaluation error occurred.
#[allow(clippy::needless_lifetimes)]
#[expect(clippy::needless_lifetimes)]
pub fn eval_entry<'tcx>(
tcx: TyCtxt<'tcx>,
entry_id: DefId,

View File

@ -156,7 +156,7 @@ pub fn iter_exported_symbols<'tcx>(
for cnum in dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| {
// We add 1 to the number because that's what rustc also does everywhere it
// calls `CrateNum::new`...
#[allow(clippy::arithmetic_side_effects)]
#[expect(clippy::arithmetic_side_effects)]
(linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1))
}) {
// We can ignore `_export_info` here: we are a Rust crate, and everything is exported

View File

@ -292,7 +292,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let b = this.read_scalar(b)?.to_f32()?;
let c = this.read_scalar(c)?.to_f32()?;
let fuse: bool = this.machine.rng.get_mut().gen();
#[allow(clippy::arithmetic_side_effects)] // float ops don't overflow
let res = if fuse {
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
@ -308,7 +307,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let b = this.read_scalar(b)?.to_f64()?;
let c = this.read_scalar(c)?.to_f64()?;
let fuse: bool = this.machine.rng.get_mut().gen();
#[allow(clippy::arithmetic_side_effects)] // float ops don't overflow
let res = if fuse {
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()

View File

@ -750,7 +750,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = if simd_element_to_bool(mask)? {
// Size * u64 is implemented as always checked
#[allow(clippy::arithmetic_side_effects)]
let ptr = ptr.wrapping_offset(dest.layout.size * i, this);
let place = this.ptr_to_mplace(ptr, dest.layout);
this.read_immediate(&place)?
@ -774,7 +773,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if simd_element_to_bool(mask)? {
// Size * u64 is implemented as always checked
#[allow(clippy::arithmetic_side_effects)]
let ptr = ptr.wrapping_offset(val.layout.size * i, this);
let place = this.ptr_to_mplace(ptr, val.layout);
this.write_immediate(*val, &place)?
@ -831,7 +829,7 @@ fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 {
assert!(idx < vec_len);
match endianness {
Endian::Little => idx,
#[allow(clippy::arithmetic_side_effects)] // idx < vec_len
#[expect(clippy::arithmetic_side_effects)] // idx < vec_len
Endian::Big => vec_len - 1 - idx, // reverse order of bits
}
}

View File

@ -89,6 +89,18 @@ impl VisitProvenance for Scalar {
}
}
impl VisitProvenance for IoError {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
use crate::shims::io_error::IoError::*;
match self {
LibcError(_name) => (),
WindowsError(_name) => (),
HostError(_io_error) => (),
Raw(scalar) => scalar.visit_provenance(visit),
}
}
}
impl VisitProvenance for Immediate<Provenance> {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
match self {

View File

@ -21,7 +21,7 @@ use crate::*;
#[derive(Debug, Copy, Clone)]
pub struct DynSym(Symbol);
#[allow(clippy::should_implement_trait)]
#[expect(clippy::should_implement_trait)]
impl DynSym {
pub fn from_str(name: &str) -> Self {
DynSym(Symbol::intern(name))
@ -648,7 +648,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = this.read_scalar(val)?.to_i32()?;
let num = this.read_target_usize(num)?;
// The docs say val is "interpreted as unsigned char".
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
#[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let val = val as u8;
// C requires that this must always be a valid pointer (C18 §7.1.4).
@ -661,7 +661,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
.position(|&c| c == val)
{
let idx = u64::try_from(idx).unwrap();
#[allow(clippy::arithmetic_side_effects)] // idx < num, so this never wraps
#[expect(clippy::arithmetic_side_effects)] // idx < num, so this never wraps
let new_ptr = ptr.wrapping_offset(Size::from_bytes(num - idx - 1), this);
this.write_pointer(new_ptr, dest)?;
} else {
@ -675,7 +675,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = this.read_scalar(val)?.to_i32()?;
let num = this.read_target_usize(num)?;
// The docs say val is "interpreted as unsigned char".
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
#[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let val = val as u8;
// C requires that this must always be a valid pointer (C18 §7.1.4).

View File

@ -7,6 +7,7 @@ use crate::*;
#[derive(Debug)]
pub enum IoError {
LibcError(&'static str),
WindowsError(&'static str),
HostError(io::Error),
Raw(Scalar),
}
@ -113,6 +114,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let errno = match err.into() {
HostError(err) => this.io_error_to_errnum(err)?,
LibcError(name) => this.eval_libc(name),
WindowsError(name) => this.eval_windows("c", name),
Raw(val) => val,
};
let errno_place = this.last_error_place()?;
@ -186,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// The inverse of `io_error_to_errnum`.
#[allow(clippy::needless_return)]
#[expect(clippy::needless_return)]
fn try_errnum_to_io_error(
&self,
errnum: Scalar,

View File

@ -53,7 +53,7 @@ impl<'tcx> Default for TlsData<'tcx> {
impl<'tcx> TlsData<'tcx> {
/// Generate a new TLS key with the given destructor.
/// `max_size` determines the integer size the key has to fit in.
#[allow(clippy::arithmetic_side_effects)]
#[expect(clippy::arithmetic_side_effects)]
pub fn create_tls_key(
&mut self,
dtor: Option<ty::Instance<'tcx>>,

View File

@ -2,6 +2,7 @@ use rustc_abi::ExternAbi;
use rustc_span::Symbol;
use crate::shims::unix::android::thread::prctl;
use crate::shims::unix::linux::syscall::syscall;
use crate::*;
pub fn is_dyn_sym(_name: &str) -> bool {
@ -26,6 +27,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}
// Dynamically invoked syscalls
"syscall" => syscall(this, link_name, abi, args, dest)?,
// Threading
"prctl" => prctl(this, link_name, abi, args, dest)?,

View File

@ -2,7 +2,7 @@ use rustc_abi::{ExternAbi, Size};
use rustc_span::Symbol;
use crate::helpers::check_min_arg_count;
use crate::shims::unix::thread::EvalContextExt as _;
use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult};
use crate::*;
const TASK_COMM_LEN: usize = 16;
@ -32,7 +32,7 @@ pub fn prctl<'tcx>(
// https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
let res =
this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
assert!(res);
assert_eq!(res, ThreadNameResult::Ok);
Scalar::from_u32(0)
}
op if op == pr_get_name => {
@ -46,7 +46,7 @@ pub fn prctl<'tcx>(
CheckInAllocMsg::MemoryAccessTest,
)?;
let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
assert!(res);
assert_eq!(res, ThreadNameResult::Ok);
Scalar::from_u32(0)
}
op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),

View File

@ -603,13 +603,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"pthread_join" => {
let [thread, retval] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
this.pthread_join(thread, retval)?;
this.write_null(dest)?;
let res = this.pthread_join(thread, retval)?;
this.write_scalar(res, dest)?;
}
"pthread_detach" => {
let [thread] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
this.pthread_detach(thread)?;
this.write_null(dest)?;
let res = this.pthread_detach(thread)?;
this.write_scalar(res, dest)?;
}
"pthread_self" => {
let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;

View File

@ -385,7 +385,7 @@ pub struct DirTable {
}
impl DirTable {
#[allow(clippy::arithmetic_side_effects)]
#[expect(clippy::arithmetic_side_effects)]
fn insert_new(&mut self, read_dir: ReadDir) -> u64 {
let id = self.next_id;
self.next_id += 1;

View File

@ -4,8 +4,7 @@ use rustc_span::Symbol;
use self::shims::unix::linux::epoll::EvalContextExt as _;
use self::shims::unix::linux::eventfd::EvalContextExt as _;
use self::shims::unix::linux::mem::EvalContextExt as _;
use self::shims::unix::linux::sync::futex;
use crate::helpers::check_min_arg_count;
use self::shims::unix::linux::syscall::syscall;
use crate::machine::{SIGRTMAX, SIGRTMIN};
use crate::shims::unix::*;
use crate::*;
@ -82,13 +81,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"pthread_setname_np" => {
let [thread, name] =
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
let res = this.pthread_setname_np(
let res = match this.pthread_setname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
TASK_COMM_LEN,
/* truncate */ false,
)?;
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
)? {
ThreadNameResult::Ok => Scalar::from_u32(0),
ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
// Act like we faild to open `/proc/self/task/$tid/comm`.
ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
};
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
@ -98,14 +101,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// In case of glibc, the length of the output buffer must
// be not shorter than TASK_COMM_LEN.
let len = this.read_scalar(len)?;
let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64
&& this.pthread_getname_np(
let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64 {
match this.pthread_getname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
len,
/* truncate*/ false,
)? {
Scalar::from_u32(0)
ThreadNameResult::Ok => Scalar::from_u32(0),
ThreadNameResult::NameTooLong => unreachable!(),
// Act like we faild to open `/proc/self/task/$tid/comm`.
ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
}
} else {
this.eval_libc("ERANGE")
};
@ -119,57 +126,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Dynamically invoked syscalls
"syscall" => {
// We do not use `check_shim` here because `syscall` is variadic. The argument
// count is checked bellow.
this.check_abi_and_shim_symbol_clash(
abi,
ExternAbi::C { unwind: false },
link_name,
)?;
// The syscall variadic function is legal to call with more arguments than needed,
// extra arguments are simply ignored. The important check is that when we use an
// argument, we have to also check all arguments *before* it to ensure that they
// have the right type.
let sys_getrandom = this.eval_libc("SYS_getrandom").to_target_usize(this)?;
let sys_futex = this.eval_libc("SYS_futex").to_target_usize(this)?;
let sys_eventfd2 = this.eval_libc("SYS_eventfd2").to_target_usize(this)?;
let [op] = check_min_arg_count("syscall", args)?;
match this.read_target_usize(op)? {
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
// is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
num if num == sys_getrandom => {
// Used by getrandom 0.1
// The first argument is the syscall id, so skip over it.
let [_, ptr, len, flags] =
check_min_arg_count("syscall(SYS_getrandom, ...)", args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
// The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
// neither of which have any effect on our current PRNG.
// See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
let _flags = this.read_scalar(flags)?.to_i32()?;
this.gen_random(ptr, len)?;
this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
}
// `futex` is used by some synchronization primitives.
num if num == sys_futex => {
futex(this, args, dest)?;
}
num if num == sys_eventfd2 => {
let [_, initval, flags] =
check_min_arg_count("syscall(SYS_evetfd2, ...)", args)?;
let result = this.eventfd(initval, flags)?;
this.write_int(result.to_i32()?, dest)?;
}
num => {
throw_unsup_format!("syscall: unsupported syscall number {num}");
}
}
syscall(this, link_name, abi, args, dest)?;
}
// Miscellaneous

View File

@ -22,7 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let flags = this.read_scalar(flags)?.to_i32()?;
// old_address must be a multiple of the page size
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
#[expect(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
this.set_last_error(LibcError("EINVAL"))?;
return interp_ok(this.eval_libc("MAP_FAILED"));

View File

@ -3,3 +3,4 @@ pub mod eventfd;
pub mod foreign_items;
pub mod mem;
pub mod sync;
pub mod syscall;

View File

@ -1,6 +1,11 @@
use crate::concurrency::sync::FutexRef;
use crate::helpers::check_min_arg_count;
use crate::*;
struct LinuxFutex {
futex: FutexRef,
}
/// Implementation of the SYS_futex syscall.
/// `args` is the arguments *including* the syscall number.
pub fn futex<'tcx>(
@ -27,7 +32,6 @@ pub fn futex<'tcx>(
// This is a vararg function so we have to bring our own type for this pointer.
let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32);
let addr_usize = addr.ptr().addr().bytes();
let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG");
let futex_wait = this.eval_libc_i32("FUTEX_WAIT");
@ -63,8 +67,7 @@ pub fn futex<'tcx>(
};
if bitset == 0 {
this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
return interp_ok(());
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
}
let timeout = this.deref_pointer_as(timeout, this.libc_ty_layout("timespec"))?;
@ -99,19 +102,18 @@ pub fn futex<'tcx>(
// effects of this and the other thread are correctly observed,
// otherwise we will deadlock.
//
// There are two scenarios to consider:
// 1. If we (FUTEX_WAIT) execute first, we'll push ourselves into
// the waiters queue and go to sleep. They (addr write & FUTEX_WAKE)
// will see us in the queue and wake us up.
// 2. If they (addr write & FUTEX_WAKE) execute first, we must observe
// addr's new value. If we see an outdated value that happens to equal
// the expected val, then we'll put ourselves to sleep with no one to wake us
// up, so we end up with a deadlock. This is prevented by having a SeqCst
// fence inside FUTEX_WAKE syscall, and another SeqCst fence
// below, the atomic read on addr after the SeqCst fence is guaranteed
// not to see any value older than the addr write immediately before
// calling FUTEX_WAKE. We'll see futex_val != val and return without
// sleeping.
// There are two scenarios to consider, depending on whether WAIT or WAKE goes first:
// 1. If we (FUTEX_WAIT) execute first, we'll push ourselves into the waiters queue and
// go to sleep. They (FUTEX_WAKE) will see us in the queue and wake us up. It doesn't
// matter how the addr write is ordered.
// 2. If they (FUTEX_WAKE) execute first, that means the addr write is also before us
// (FUTEX_WAIT). It is crucial that we observe addr's new value. If we see an
// outdated value that happens to equal the expected val, then we'll put ourselves to
// sleep with no one to wake us up, so we end up with a deadlock. This is prevented
// by having a SeqCst fence inside FUTEX_WAKE syscall, and another SeqCst fence here
// in FUTEX_WAIT. The atomic read on addr after the SeqCst fence is guaranteed not to
// see any value older than the addr write immediately before calling FUTEX_WAKE.
// We'll see futex_val != val and return without sleeping.
//
// Note that the fences do not create any happens-before relationship.
// The read sees the write immediately before the fence not because
@ -140,17 +142,28 @@ pub fn futex<'tcx>(
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
// Read an `i32` through the pointer, regardless of any wrapper types.
// It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Relaxed)?.to_i32()?;
// We do an acquire read -- it only seems reasonable that if we observe a value here, we
// actually establish an ordering with that value.
let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Acquire)?.to_i32()?;
if val == futex_val {
// The value still matches, so we block the thread and make it wait for FUTEX_WAKE.
// This cannot fail since we already did an atomic acquire read on that pointer.
// Acquire reads are only allowed on mutable memory.
let futex_ref = this
.get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
.unwrap()
.futex
.clone();
this.futex_wait(
addr_usize,
futex_ref,
bitset,
timeout,
Scalar::from_target_isize(0, this), // retval_succ
Scalar::from_target_isize(-1, this), // retval_timeout
dest.clone(),
this.eval_libc("ETIMEDOUT"), // errno_timeout
LibcError("ETIMEDOUT"), // errno_timeout
);
} else {
// The futex value doesn't match the expected value, so we return failure
@ -165,6 +178,17 @@ pub fn futex<'tcx>(
// FUTEX_WAKE_BITSET: (int *addr, int op = FUTEX_WAKE, int val, const timespect *_unused, int *_unused, unsigned int bitset)
// Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up.
op if op == futex_wake || op == futex_wake_bitset => {
let Some(futex_ref) =
this.get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
else {
// No AllocId, or no live allocation at that AllocId.
// Return an error code. (That seems nicer than silently doing something non-intuitive.)
// This means that if an address gets reused by a new allocation,
// we'll use an independent futex queue for this... that seems acceptable.
return this.set_last_error_and_return(LibcError("EFAULT"), dest);
};
let futex_ref = futex_ref.futex.clone();
let bitset = if op == futex_wake_bitset {
let [_, _, _, _, timeout, uaddr2, bitset] =
check_min_arg_count("`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`", args)?;
@ -182,9 +206,9 @@ pub fn futex<'tcx>(
// before doing the syscall.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
let mut n = 0;
#[allow(clippy::arithmetic_side_effects)]
#[expect(clippy::arithmetic_side_effects)]
for _ in 0..val {
if this.futex_wake(addr_usize, bitset)? {
if this.futex_wake(&futex_ref, bitset)? {
n += 1;
} else {
break;

View File

@ -0,0 +1,63 @@
use rustc_abi::ExternAbi;
use rustc_span::Symbol;
use self::shims::unix::linux::eventfd::EvalContextExt as _;
use crate::helpers::check_min_arg_count;
use crate::shims::unix::linux::sync::futex;
use crate::*;
pub fn syscall<'tcx>(
this: &mut MiriInterpCx<'tcx>,
link_name: Symbol,
abi: ExternAbi,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// We do not use `check_shim` here because `syscall` is variadic. The argument
// count is checked bellow.
this.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
// The syscall variadic function is legal to call with more arguments than needed,
// extra arguments are simply ignored. The important check is that when we use an
// argument, we have to also check all arguments *before* it to ensure that they
// have the right type.
let sys_getrandom = this.eval_libc("SYS_getrandom").to_target_usize(this)?;
let sys_futex = this.eval_libc("SYS_futex").to_target_usize(this)?;
let sys_eventfd2 = this.eval_libc("SYS_eventfd2").to_target_usize(this)?;
let [op] = check_min_arg_count("syscall", args)?;
match this.read_target_usize(op)? {
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
// is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
num if num == sys_getrandom => {
// Used by getrandom 0.1
// The first argument is the syscall id, so skip over it.
let [_, ptr, len, flags] = check_min_arg_count("syscall(SYS_getrandom, ...)", args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
// The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
// neither of which have any effect on our current PRNG.
// See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
let _flags = this.read_scalar(flags)?.to_i32()?;
this.gen_random(ptr, len)?;
this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
}
// `futex` is used by some synchronization primitives.
num if num == sys_futex => {
futex(this, args, dest)?;
}
num if num == sys_eventfd2 => {
let [_, initval, flags] = check_min_arg_count("syscall(SYS_evetfd2, ...)", args)?;
let result = this.eventfd(initval, flags)?;
this.write_int(result.to_i32()?, dest)?;
}
num => {
throw_unsup_format!("syscall: unsupported syscall number {num}");
}
};
interp_ok(())
}

View File

@ -181,18 +181,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// are met, then the name is set and 0 is returned. Otherwise, if
// the specified name is lomnger than MAXTHREADNAMESIZE, then
// ENAMETOOLONG is returned.
//
// FIXME: the real implementation maybe returns ESRCH if the thread ID is invalid.
let thread = this.pthread_self()?;
let res = if this.pthread_setname_np(
let res = match this.pthread_setname_np(
thread,
this.read_scalar(name)?,
this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
/* truncate */ false,
)? {
Scalar::from_u32(0)
} else {
this.eval_libc("ENAMETOOLONG")
ThreadNameResult::Ok => Scalar::from_u32(0),
ThreadNameResult::NameTooLong => this.eval_libc("ENAMETOOLONG"),
ThreadNameResult::ThreadNotFound => unreachable!(),
};
// Contrary to the manpage, `pthread_setname_np` on macOS still
// returns an integer indicating success.
@ -210,15 +208,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1160-L1175.
// The key part is the strlcpy, which truncates the resulting value,
// but always null terminates (except for zero sized buffers).
//
// FIXME: the real implementation returns ESRCH if the thread ID is invalid.
let res = Scalar::from_u32(0);
this.pthread_getname_np(
let res = match this.pthread_getname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
this.read_scalar(len)?,
/* truncate */ true,
)?;
)? {
ThreadNameResult::Ok => Scalar::from_u32(0),
// `NameTooLong` is possible when the buffer is zero sized,
ThreadNameResult::NameTooLong => Scalar::from_u32(0),
ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
};
this.write_scalar(res, dest)?;
}

View File

@ -132,7 +132,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// addr must be a multiple of the page size, but apart from that munmap is just implemented
// as a dealloc.
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
#[expect(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
if addr.addr().bytes() % this.machine.page_size != 0 {
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
}

View File

@ -21,7 +21,7 @@ pub use self::fs::{DirTable, EvalContextExt as _};
pub use self::linux::epoll::EpollInterestTable;
pub use self::mem::EvalContextExt as _;
pub use self::sync::EvalContextExt as _;
pub use self::thread::EvalContextExt as _;
pub use self::thread::{EvalContextExt as _, ThreadNameResult};
pub use self::unnamed_socket::EvalContextExt as _;
// Make up some constants.

View File

@ -26,26 +26,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// THREAD_NAME_MAX allows a thread name of 31+1 length
// https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613
let max_len = 32;
let res = this.pthread_setname_np(
// See https://illumos.org/man/3C/pthread_setname_np for the error codes.
let res = match this.pthread_setname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
max_len,
/* truncate */ false,
)?;
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
)? {
ThreadNameResult::Ok => Scalar::from_u32(0),
ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
};
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] =
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
// https://github.com/illumos/illumos-gate/blob/c56822be04b6c157c8b6f2281e47214c3b86f657/usr/src/lib/libc/port/threads/thr.c#L2449-L2480
let res = this.pthread_getname_np(
// See https://illumos.org/man/3C/pthread_getname_np for the error codes.
let res = match this.pthread_getname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
this.read_scalar(len)?,
/* truncate */ false,
)?;
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
)? {
ThreadNameResult::Ok => Scalar::from_u32(0),
ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
};
this.write_scalar(res, dest)?;
}

View File

@ -685,7 +685,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let id = rwlock_get_data(this, rwlock_op)?.id;
#[allow(clippy::if_same_then_else)]
if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? {
interp_ok(())
} else {

View File

@ -2,6 +2,13 @@ use rustc_abi::ExternAbi;
use crate::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ThreadNameResult {
Ok,
NameTooLong,
ThreadNotFound,
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_create(
@ -30,7 +37,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(())
}
fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
fn pthread_join(
&mut self,
thread: &OpTy<'tcx>,
retval: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
if !this.ptr_is_null(this.read_pointer(retval)?)? {
@ -38,22 +49,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
}
let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?;
let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
let Ok(thread) = this.thread_id_try_from(thread) else {
return interp_ok(this.eval_libc("ESRCH"));
};
interp_ok(())
this.join_thread_exclusive(thread)?;
interp_ok(Scalar::from_u32(0))
}
fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
this.detach_thread(
thread_id.try_into().expect("thread ID should fit in u32"),
/*allow_terminated_joined*/ false,
)?;
let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
let Ok(thread) = this.thread_id_try_from(thread) else {
return interp_ok(this.eval_libc("ESRCH"));
};
this.detach_thread(thread, /*allow_terminated_joined*/ false)?;
interp_ok(())
interp_ok(Scalar::from_u32(0))
}
fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
@ -65,18 +80,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Set the name of the specified thread. If the name including the null terminator
/// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
/// is used as the thread name, otherwise `false` is returned.
/// is used as the thread name, otherwise [`ThreadNameResult::NameTooLong`] is returned.
/// If the specified thread wasn't found, [`ThreadNameResult::ThreadNotFound`] is returned.
fn pthread_setname_np(
&mut self,
thread: Scalar,
name: Scalar,
name_max_len: usize,
truncate: bool,
) -> InterpResult<'tcx, bool> {
) -> InterpResult<'tcx, ThreadNameResult> {
let this = self.eval_context_mut();
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
let thread = ThreadId::try_from(thread).unwrap();
let Ok(thread) = this.thread_id_try_from(thread) else {
return interp_ok(ThreadNameResult::ThreadNotFound);
};
let name = name.to_pointer(this)?;
let mut name = this.read_c_str(name)?.to_owned();
@ -85,29 +103,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if truncate {
name.truncate(name_max_len.saturating_sub(1));
} else {
return interp_ok(false);
return interp_ok(ThreadNameResult::NameTooLong);
}
}
this.set_thread_name(thread, name);
interp_ok(true)
interp_ok(ThreadNameResult::Ok)
}
/// Get the name of the specified thread. If the thread name doesn't fit
/// the buffer, then if `truncate` is set the truncated name is written out,
/// otherwise `false` is returned.
/// otherwise [`ThreadNameResult::NameTooLong`] is returned. If the specified
/// thread wasn't found, [`ThreadNameResult::ThreadNotFound`] is returned.
fn pthread_getname_np(
&mut self,
thread: Scalar,
name_out: Scalar,
len: Scalar,
truncate: bool,
) -> InterpResult<'tcx, bool> {
) -> InterpResult<'tcx, ThreadNameResult> {
let this = self.eval_context_mut();
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
let thread = ThreadId::try_from(thread).unwrap();
let Ok(thread) = this.thread_id_try_from(thread) else {
return interp_ok(ThreadNameResult::ThreadNotFound);
};
let name_out = name_out.to_pointer(this)?;
let len = len.to_target_usize(this)?;
@ -119,8 +140,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let (success, _written) = this.write_c_str(name, name_out, len)?;
let res = if success { ThreadNameResult::Ok } else { ThreadNameResult::NameTooLong };
interp_ok(success)
interp_ok(res)
}
fn sched_yield(&mut self) -> InterpResult<'tcx, ()> {

View File

@ -10,6 +10,10 @@ use crate::shims::os_str::bytes_to_os_str;
use crate::shims::windows::*;
use crate::*;
// The NTSTATUS STATUS_INVALID_HANDLE (0xC0000008) encoded as a HRESULT by setting the N bit.
// (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a)
const STATUS_INVALID_HANDLE: u32 = 0xD0000008;
pub fn is_dyn_sym(name: &str) -> bool {
// std does dynamic detection for these symbols
matches!(
@ -25,7 +29,7 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
}
#[cfg(unix)]
#[allow(clippy::get_first, clippy::arithmetic_side_effects)]
#[expect(clippy::get_first, clippy::arithmetic_side_effects)]
fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
// We are on Unix, so we need to implement parts of the logic ourselves.
let bytes = path.as_os_str().as_encoded_bytes();
@ -484,14 +488,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let thread_id =
this.CreateThread(security, stacksize, start, arg, flags, thread)?;
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
this.write_scalar(Handle::Thread(thread_id.to_u32()).to_scalar(this), dest)?;
}
"WaitForSingleObject" => {
let [handle, timeout] =
this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
let ret = this.WaitForSingleObject(handle, timeout)?;
this.write_scalar(Scalar::from_u32(ret), dest)?;
this.write_scalar(ret, dest)?;
}
"GetCurrentThread" => {
let [] =
@ -510,15 +514,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let name = this.read_wide_str(this.read_pointer(name)?)?;
let thread = match Handle::from_scalar(handle, this)? {
Some(Handle::Thread(thread)) => thread,
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
Some(Handle::Thread(thread)) => this.thread_id_try_from(thread),
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
_ => this.invalid_handle("SetThreadDescription")?,
};
let res = match thread {
Ok(thread) => {
// FIXME: use non-lossy conversion
this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
Scalar::from_u32(0)
}
Err(_) => Scalar::from_u32(STATUS_INVALID_HANDLE),
};
// FIXME: use non-lossy conversion
this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
this.write_null(dest)?;
this.write_scalar(res, dest)?;
}
"GetThreadDescription" => {
let [handle, name_ptr] =
@ -528,20 +537,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
let thread = match Handle::from_scalar(handle, this)? {
Some(Handle::Thread(thread)) => thread,
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
_ => this.invalid_handle("SetThreadDescription")?,
Some(Handle::Thread(thread)) => this.thread_id_try_from(thread),
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
_ => this.invalid_handle("GetThreadDescription")?,
};
let (name, res) = match thread {
Ok(thread) => {
// Looks like the default thread name is empty.
let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
let name = this.alloc_os_str_as_wide_str(
bytes_to_os_str(&name)?,
MiriMemoryKind::WinLocal.into(),
)?;
(Scalar::from_maybe_pointer(name, this), Scalar::from_u32(0))
}
Err(_) => (Scalar::null_ptr(this), Scalar::from_u32(STATUS_INVALID_HANDLE)),
};
// Looks like the default thread name is empty.
let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
let name = this.alloc_os_str_as_wide_str(
bytes_to_os_str(&name)?,
MiriMemoryKind::WinLocal.into(),
)?;
this.write_scalar(Scalar::from_maybe_pointer(name, this), &name_ptr)?;
this.write_null(dest)?;
this.write_scalar(name, &name_ptr)?;
this.write_scalar(res, dest)?;
}
// Miscellaneous
@ -630,9 +644,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let [handle] =
this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
this.CloseHandle(handle)?;
let ret = this.CloseHandle(handle)?;
this.write_int(1, dest)?;
this.write_scalar(ret, dest)?;
}
"GetModuleFileNameW" => {
let [handle, filename, size] =

View File

@ -14,7 +14,7 @@ pub enum PseudoHandle {
pub enum Handle {
Null,
Pseudo(PseudoHandle),
Thread(ThreadId),
Thread(u32),
}
impl PseudoHandle {
@ -51,7 +51,7 @@ impl Handle {
match self {
Self::Null => 0,
Self::Pseudo(pseudo_handle) => pseudo_handle.value(),
Self::Thread(thread) => thread.to_u32(),
Self::Thread(thread) => thread,
}
}
@ -63,7 +63,7 @@ impl Handle {
let floor_log2 = variant_count.ilog2();
// we need to add one for non powers of two to compensate for the difference
#[allow(clippy::arithmetic_side_effects)] // cannot overflow
#[expect(clippy::arithmetic_side_effects)] // cannot overflow
if variant_count.is_power_of_two() { floor_log2 } else { floor_log2 + 1 }
}
@ -88,15 +88,14 @@ impl Handle {
// packs the data into the lower `data_size` bits
// and packs the discriminant right above the data
#[allow(clippy::arithmetic_side_effects)] // cannot overflow
return discriminant << data_size | data;
discriminant << data_size | data
}
fn new(discriminant: u32, data: u32) -> Option<Self> {
match discriminant {
Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
Self::THREAD_DISCRIMINANT => Some(Self::Thread(data.into())),
Self::THREAD_DISCRIMINANT => Some(Self::Thread(data)),
_ => None,
}
}
@ -107,11 +106,10 @@ impl Handle {
let data_size = u32::BITS.strict_sub(disc_size);
// the lower `data_size` bits of this mask are 1
#[allow(clippy::arithmetic_side_effects)] // cannot overflow
#[expect(clippy::arithmetic_side_effects)] // cannot overflow
let data_mask = 2u32.pow(data_size) - 1;
// the discriminant is stored right above the lower `data_size` bits
#[allow(clippy::arithmetic_side_effects)] // cannot overflow
let discriminant = handle >> data_size;
// the data is stored in the lower `data_size` bits
@ -123,7 +121,7 @@ impl Handle {
pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar {
// 64-bit handles are sign extended 32-bit handles
// see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
#[allow(clippy::cast_possible_wrap)] // we want it to wrap
#[expect(clippy::cast_possible_wrap)] // we want it to wrap
let signed_handle = self.to_packed() as i32;
Scalar::from_target_isize(signed_handle.into(), cx)
}
@ -134,7 +132,7 @@ impl Handle {
) -> InterpResult<'tcx, Option<Self>> {
let sign_extended_handle = handle.to_target_isize(cx)?;
#[allow(clippy::cast_sign_loss)] // we want to lose the sign
#[expect(clippy::cast_sign_loss)] // we want to lose the sign
let handle = if let Ok(signed_handle) = i32::try_from(sign_extended_handle) {
signed_handle as u32
} else {
@ -156,17 +154,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
)))
}
fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let handle = this.read_scalar(handle_op)?;
match Handle::from_scalar(handle, this)? {
Some(Handle::Thread(thread)) =>
this.detach_thread(thread, /*allow_terminated_joined*/ true)?,
let ret = match Handle::from_scalar(handle, this)? {
Some(Handle::Thread(thread)) => {
if let Ok(thread) = this.thread_id_try_from(thread) {
this.detach_thread(thread, /*allow_terminated_joined*/ true)?;
this.eval_windows("c", "TRUE")
} else {
this.invalid_handle("CloseHandle")?
}
}
_ => this.invalid_handle("CloseHandle")?,
}
};
interp_ok(())
interp_ok(ret)
}
}

View File

@ -3,6 +3,7 @@ use std::time::Duration;
use rustc_abi::Size;
use crate::concurrency::init_once::InitOnceStatus;
use crate::concurrency::sync::FutexRef;
use crate::*;
#[derive(Copy, Clone)]
@ -10,6 +11,10 @@ struct WindowsInitOnce {
id: InitOnceId,
}
struct WindowsFutex {
futex: FutexRef,
}
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Windows sync primitives are pointer sized.
@ -168,8 +173,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let size = this.read_target_usize(size_op)?;
let timeout_ms = this.read_scalar(timeout_op)?.to_u32()?;
let addr = ptr.addr().bytes();
if size > 8 || !size.is_power_of_two() {
let invalid_param = this.eval_windows("c", "ERROR_INVALID_PARAMETER");
this.set_last_error(invalid_param)?;
@ -190,19 +193,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let layout = this.machine.layouts.uint(size).unwrap();
let futex_val =
this.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Relaxed)?;
this.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Acquire)?;
let compare_val = this.read_scalar(&this.ptr_to_mplace(compare, layout))?;
if futex_val == compare_val {
// If the values are the same, we have to block.
// This cannot fail since we already did an atomic acquire read on that pointer.
let futex_ref = this
.get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
.unwrap()
.futex
.clone();
this.futex_wait(
addr,
futex_ref,
u32::MAX, // bitset
timeout,
Scalar::from_i32(1), // retval_succ
Scalar::from_i32(0), // retval_timeout
dest.clone(),
this.eval_windows("c", "ERROR_TIMEOUT"), // errno_timeout
IoError::WindowsError("ERROR_TIMEOUT"), // errno_timeout
);
}
@ -219,8 +230,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// See the Linux futex implementation for why this fence exists.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
let addr = ptr.addr().bytes();
this.futex_wake(addr, u32::MAX)?;
let Some(futex_ref) =
this.get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
else {
// Seems like this cannot return an error, so we just wake nobody.
return interp_ok(());
};
let futex_ref = futex_ref.futex.clone();
this.futex_wake(&futex_ref, u32::MAX)?;
interp_ok(())
}
@ -232,8 +250,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// See the Linux futex implementation for why this fence exists.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
let addr = ptr.addr().bytes();
while this.futex_wake(addr, u32::MAX)? {}
let Some(futex_ref) =
this.get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
else {
// Seems like this cannot return an error, so we just wake nobody.
return interp_ok(());
};
let futex_ref = futex_ref.futex.clone();
while this.futex_wake(&futex_ref, u32::MAX)? {}
interp_ok(())
}

View File

@ -59,14 +59,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
handle_op: &OpTy<'tcx>,
timeout_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, u32> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let handle = this.read_scalar(handle_op)?;
let timeout = this.read_scalar(timeout_op)?.to_u32()?;
let thread = match Handle::from_scalar(handle, this)? {
Some(Handle::Thread(thread)) => thread,
Some(Handle::Thread(thread)) =>
match this.thread_id_try_from(thread) {
Ok(thread) => thread,
Err(_) => this.invalid_handle("WaitForSingleObject")?,
},
// Unlike on posix, the outcome of joining the current thread is not documented.
// On current Windows, it just deadlocks.
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
@ -79,6 +83,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.join_thread(thread)?;
interp_ok(0)
interp_ok(this.eval_windows("c", "WAIT_OBJECT_0"))
}
}

View File

@ -136,7 +136,7 @@ fn affine_transform<'tcx>(
// This is a evaluated at compile time. Trait based conversion is not available.
/// See <https://www.corsix.org/content/galois-field-instructions-2021-cpus> for the
/// definition of `gf_inv` which was used for the creation of this table.
#[allow(clippy::cast_possible_truncation)]
#[expect(clippy::cast_possible_truncation)]
static TABLE: [u8; 256] = {
let mut array = [0; 256];
@ -163,7 +163,7 @@ static TABLE: [u8; 256] = {
/// polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1.
/// See <https://www.corsix.org/content/galois-field-instructions-2021-cpus> for details.
// This is a const function. Trait based conversion is not available.
#[allow(clippy::cast_possible_truncation)]
#[expect(clippy::cast_possible_truncation)]
const fn gf2p8_mul(left: u8, right: u8) -> u8 {
// This implementation is based on the `gf2p8mul_byte` definition found inside the Intel intrinsics guide.
// See https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul

View File

@ -95,11 +95,22 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"pclmulqdq" => {
"pclmulqdq" | "pclmulqdq.256" | "pclmulqdq.512" => {
let mut len = 2; // in units of 64bits
this.expect_target_feature_for_intrinsic(link_name, "pclmulqdq")?;
if unprefixed_name.ends_with(".256") {
this.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
len = 4;
} else if unprefixed_name.ends_with(".512") {
this.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
this.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
len = 8;
}
let [left, right, imm] =
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
pclmulqdq(this, left, right, imm, dest)?;
pclmulqdq(this, left, right, imm, dest, len)?;
}
name if name.starts_with("bmi.") => {
@ -386,7 +397,6 @@ enum FloatUnaryOp {
}
/// Performs `which` scalar operation on `op` and returns the result.
#[allow(clippy::arithmetic_side_effects)] // floating point operations without side effects
fn unary_op_f32<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
which: FloatUnaryOp,
@ -415,7 +425,7 @@ fn unary_op_f32<'tcx>(
}
/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
#[allow(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic
#[expect(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic
fn apply_random_float_error<F: rustc_apfloat::Float>(
this: &mut crate::MiriInterpCx<'_>,
val: F,
@ -1122,7 +1132,7 @@ fn pmulhrsw<'tcx>(
// The result of this operation can overflow a signed 16-bit integer.
// When `left` and `right` are -0x8000, the result is 0x8000.
#[allow(clippy::cast_possible_truncation)]
#[expect(clippy::cast_possible_truncation)]
let res = res as i16;
this.write_scalar(Scalar::from_i16(res), &dest)?;
@ -1134,9 +1144,12 @@ fn pmulhrsw<'tcx>(
/// Perform a carry-less multiplication of two 64-bit integers, selected from `left` and `right` according to `imm8`,
/// and store the results in `dst`.
///
/// `left` and `right` are both vectors of type 2 x i64. Only bits 0 and 4 of `imm8` matter;
/// `left` and `right` are both vectors of type `len` x i64. Only bits 0 and 4 of `imm8` matter;
/// they select the element of `left` and `right`, respectively.
///
/// `len` is the SIMD vector length (in counts of `i64` values). It is expected to be one of
/// `2`, `4`, or `8`.
///
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128>
fn pclmulqdq<'tcx>(
this: &mut MiriInterpCx<'tcx>,
@ -1144,52 +1157,56 @@ fn pclmulqdq<'tcx>(
right: &OpTy<'tcx>,
imm8: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
len: u64,
) -> InterpResult<'tcx, ()> {
assert_eq!(left.layout, right.layout);
assert_eq!(left.layout.size, dest.layout.size);
assert!([2u64, 4, 8].contains(&len));
// Transmute to `[u64; 2]`
// Transmute the input into arrays of `[u64; len]`.
// Transmute the output into an array of `[u128, len / 2]`.
let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?;
let left = left.transmute(array_layout, this)?;
let right = right.transmute(array_layout, this)?;
let dest = dest.transmute(array_layout, this)?;
let src_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, len))?;
let dest_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u128, len / 2))?;
let left = left.transmute(src_layout, this)?;
let right = right.transmute(src_layout, this)?;
let dest = dest.transmute(dest_layout, this)?;
let imm8 = this.read_scalar(imm8)?.to_u8()?;
// select the 64-bit integer from left that the user specified (low or high)
let index = if (imm8 & 0x01) == 0 { 0 } else { 1 };
let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u64()?;
for i in 0..(len / 2) {
let lo = i.strict_mul(2);
let hi = i.strict_mul(2).strict_add(1);
// select the 64-bit integer from right that the user specified (low or high)
let index = if (imm8 & 0x10) == 0 { 0 } else { 1 };
let right = this.read_scalar(&this.project_index(&right, index)?)?.to_u64()?;
// select the 64-bit integer from left that the user specified (low or high)
let index = if (imm8 & 0x01) == 0 { lo } else { hi };
let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u64()?;
// Perform carry-less multiplication
//
// This operation is like long multiplication, but ignores all carries.
// That idea corresponds to the xor operator, which is used in the implementation.
//
// Wikipedia has an example https://en.wikipedia.org/wiki/Carry-less_product#Example
let mut result: u128 = 0;
// select the 64-bit integer from right that the user specified (low or high)
let index = if (imm8 & 0x10) == 0 { lo } else { hi };
let right = this.read_scalar(&this.project_index(&right, index)?)?.to_u64()?;
for i in 0..64 {
// if the i-th bit in right is set
if (right & (1 << i)) != 0 {
// xor result with `left` shifted to the left by i positions
result ^= u128::from(left) << i;
// Perform carry-less multiplication.
//
// This operation is like long multiplication, but ignores all carries.
// That idea corresponds to the xor operator, which is used in the implementation.
//
// Wikipedia has an example https://en.wikipedia.org/wiki/Carry-less_product#Example
let mut result: u128 = 0;
for i in 0..64 {
// if the i-th bit in right is set
if (right & (1 << i)) != 0 {
// xor result with `left` shifted to the left by i positions
result ^= u128::from(left) << i;
}
}
let dest = this.project_index(&dest, i)?;
this.write_scalar(Scalar::from_u128(result), &dest)?;
}
let result_low = (result & 0xFFFF_FFFF_FFFF_FFFF) as u64;
let result_high = (result >> 64) as u64;
let dest_low = this.project_index(&dest, 0)?;
this.write_scalar(Scalar::from_u64(result_low), &dest_low)?;
let dest_high = this.project_index(&dest, 1)?;
this.write_scalar(Scalar::from_u64(result_high), &dest_high)?;
interp_ok(())
}

View File

@ -68,7 +68,7 @@ const USE_SIGNED: u8 = 2;
/// The mask may be negated if negation flags inside the immediate byte are set.
///
/// For more information, see the Intel Software Developer's Manual, Vol. 2b, Chapter 4.1.
#[allow(clippy::arithmetic_side_effects)]
#[expect(clippy::arithmetic_side_effects)]
fn compare_strings<'tcx>(
this: &mut MiriInterpCx<'tcx>,
str1: &OpTy<'tcx>,
@ -444,7 +444,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let crc = if bit_size == 64 {
// The 64-bit version will only consider the lower 32 bits,
// while the upper 32 bits get discarded.
#[allow(clippy::cast_possible_truncation)]
#[expect(clippy::cast_possible_truncation)]
u128::from((left.to_u64()? as u32).reverse_bits())
} else {
u128::from(left.to_u32()?.reverse_bits())

View File

@ -1,4 +1,4 @@
//@only-target: linux
//@only-target: linux android
//@compile-flags: -Zmiri-disable-isolation
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
@ -7,8 +7,8 @@
use std::mem::MaybeUninit;
use std::ptr::{self, addr_of};
use std::sync::atomic::{AtomicI32, Ordering};
use std::thread;
use std::time::{Duration, Instant};
use std::{io, thread};
fn wake_nobody() {
let futex = 0;
@ -40,9 +40,12 @@ fn wake_dangling() {
let ptr: *const i32 = &*futex;
drop(futex);
// Wake 1 waiter. Expect zero waiters woken up, as nobody is waiting.
// Expect error since this is now "unmapped" memory.
// parking_lot relies on this:
// <https://github.com/Amanieu/parking_lot/blob/ca920b31312839013b4455aba1d53a4aede21b2f/core/src/thread_parker/linux.rs#L138-L145>
unsafe {
assert_eq!(libc::syscall(libc::SYS_futex, ptr, libc::FUTEX_WAKE, 1), 0);
assert_eq!(libc::syscall(libc::SYS_futex, ptr, libc::FUTEX_WAKE, 1), -1);
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::EFAULT);
}
}

View File

@ -28,26 +28,27 @@ fn test_getrandom() {
let mut buf = [0u8; 5];
unsafe {
#[cfg(target_os = "linux")]
assert_eq!(
libc::syscall(
libc::SYS_getrandom,
ptr::null_mut::<libc::c_void>(),
0 as libc::size_t,
0 as libc::c_uint,
),
0,
);
#[cfg(target_os = "linux")]
assert_eq!(
libc::syscall(
libc::SYS_getrandom,
buf.as_mut_ptr() as *mut libc::c_void,
5 as libc::size_t,
0 as libc::c_uint,
),
5,
);
#[cfg(any(target_os = "linux", target_os = "android"))]
{
assert_eq!(
libc::syscall(
libc::SYS_getrandom,
ptr::null_mut::<libc::c_void>(),
0 as libc::size_t,
0 as libc::c_uint,
),
0,
);
assert_eq!(
libc::syscall(
libc::SYS_getrandom,
buf.as_mut_ptr() as *mut libc::c_void,
5 as libc::size_t,
0 as libc::c_uint,
),
5,
);
}
assert_eq!(
libc::getrandom(ptr::null_mut::<libc::c_void>(), 0 as libc::size_t, 0 as libc::c_uint),

View File

@ -22,6 +22,22 @@ fn main() {
check_condattr();
}
// We want to only use pthread APIs here for easier testing.
// So we can't use `thread::scope`. That means panics can lead
// to a failure to join threads which can lead to further issues,
// so let's turn such unwinding into aborts.
struct AbortOnDrop;
impl AbortOnDrop {
fn defuse(self) {
mem::forget(self);
}
}
impl Drop for AbortOnDrop {
fn drop(&mut self) {
std::process::abort();
}
}
fn test_mutex_libc_init_recursive() {
unsafe {
let mut attr: libc::pthread_mutexattr_t = mem::zeroed();
@ -122,6 +138,7 @@ impl<T> Clone for SendPtr<T> {
}
fn check_mutex() {
let bomb = AbortOnDrop;
// Specifically *not* using `Arc` to make sure there is no synchronization apart from the mutex.
unsafe {
let data = SyncUnsafeCell::new((libc::PTHREAD_MUTEX_INITIALIZER, 0));
@ -148,9 +165,11 @@ fn check_mutex() {
assert_eq!(libc::pthread_mutex_trylock(mutexptr), 0);
assert_eq!((*ptr.ptr).1, 3);
}
bomb.defuse();
}
fn check_rwlock_write() {
let bomb = AbortOnDrop;
unsafe {
let data = SyncUnsafeCell::new((libc::PTHREAD_RWLOCK_INITIALIZER, 0));
let ptr = SendPtr { ptr: data.get() };
@ -187,9 +206,11 @@ fn check_rwlock_write() {
assert_eq!(libc::pthread_rwlock_tryrdlock(rwlockptr), 0);
assert_eq!((*ptr.ptr).1, 3);
}
bomb.defuse();
}
fn check_rwlock_read_no_deadlock() {
let bomb = AbortOnDrop;
unsafe {
let l1 = SyncUnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
let l1 = SendPtr { ptr: l1.get() };
@ -213,9 +234,11 @@ fn check_rwlock_read_no_deadlock() {
assert_eq!(libc::pthread_rwlock_rdlock(l2.ptr), 0);
handle.join().unwrap();
}
bomb.defuse();
}
fn check_cond() {
let bomb = AbortOnDrop;
unsafe {
let mut cond: MaybeUninit<libc::pthread_cond_t> = MaybeUninit::uninit();
assert_eq!(libc::pthread_cond_init(cond.as_mut_ptr(), ptr::null()), 0);
@ -260,6 +283,7 @@ fn check_cond() {
t.join().unwrap();
}
bomb.defuse();
}
fn check_condattr() {

View File

@ -199,4 +199,29 @@ fn main() {
.unwrap()
.join()
.unwrap();
// Now set the name for a non-existing thread and verify error codes.
// (FreeBSD doesn't return an error code.)
#[cfg(not(target_os = "freebsd"))]
{
let invalid_thread = 0xdeadbeef;
let error = {
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
libc::ENOENT
} else {
libc::ESRCH
}
}
};
#[cfg(not(target_os = "macos"))]
{
// macOS has no `setname` function accepting a thread id as the first argument.
let res = unsafe { libc::pthread_setname_np(invalid_thread, [0].as_ptr()) };
assert_eq!(res, error);
}
let mut buf = [0; 64];
let res = unsafe { libc::pthread_getname_np(invalid_thread, buf.as_mut_ptr(), buf.len()) };
assert_eq!(res, error);
}
}

View File

@ -181,7 +181,7 @@ unsafe fn schedule(v0: __m128i, v1: __m128i, v2: __m128i, v3: __m128i) -> __m128
}
// we use unaligned loads with `__m128i` pointers
#[allow(clippy::cast_ptr_alignment)]
#[expect(clippy::cast_ptr_alignment)]
#[target_feature(enable = "sha,sse2,ssse3,sse4.1")]
unsafe fn digest_blocks(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
#[allow(non_snake_case)]

View File

@ -0,0 +1,193 @@
// We're testing x86 target specific features
//@revisions: avx512 avx
//@only-target: x86_64 i686
//@[avx512]compile-flags: -C target-feature=+vpclmulqdq,+avx512f
//@[avx]compile-flags: -C target-feature=+vpclmulqdq,+avx2
// The constants in the tests below are just bit patterns. They should not
// be interpreted as integers; signedness does not make sense for them, but
// __mXXXi happens to be defined in terms of signed integers.
#![allow(overflowing_literals)]
#![feature(avx512_target_feature)]
#![feature(stdarch_x86_avx512)]
#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
use std::mem::transmute;
fn main() {
// Mostly copied from library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs
assert!(is_x86_feature_detected!("pclmulqdq"));
assert!(is_x86_feature_detected!("vpclmulqdq"));
unsafe {
test_mm256_clmulepi64_epi128();
if is_x86_feature_detected!("avx512f") {
test_mm512_clmulepi64_epi128();
}
}
}
macro_rules! verify_kat_pclmul {
($broadcast:ident, $clmul:ident, $assert:ident) => {
// Constants taken from https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf
let a = _mm_set_epi64x(0x7b5b546573745665, 0x63746f725d53475d);
let a = $broadcast(a);
let b = _mm_set_epi64x(0x4869285368617929, 0x5b477565726f6e5d);
let b = $broadcast(b);
let r00 = _mm_set_epi64x(0x1d4d84c85c3440c0, 0x929633d5d36f0451);
let r00 = $broadcast(r00);
let r01 = _mm_set_epi64x(0x1bd17c8d556ab5a1, 0x7fa540ac2a281315);
let r01 = $broadcast(r01);
let r10 = _mm_set_epi64x(0x1a2bf6db3a30862f, 0xbabf262df4b7d5c9);
let r10 = $broadcast(r10);
let r11 = _mm_set_epi64x(0x1d1e1f2c592e7c45, 0xd66ee03e410fd4ed);
let r11 = $broadcast(r11);
$assert($clmul::<0x00>(a, b), r00);
$assert($clmul::<0x10>(a, b), r01);
$assert($clmul::<0x01>(a, b), r10);
$assert($clmul::<0x11>(a, b), r11);
let a0 = _mm_set_epi64x(0x0000000000000000, 0x8000000000000000);
let a0 = $broadcast(a0);
let r = _mm_set_epi64x(0x4000000000000000, 0x0000000000000000);
let r = $broadcast(r);
$assert($clmul::<0x00>(a0, a0), r);
}
}
// this function tests one of the possible 4 instances
// with different inputs across lanes for the 512-bit version
#[target_feature(enable = "vpclmulqdq,avx512f")]
unsafe fn verify_512_helper(
linear: unsafe fn(__m128i, __m128i) -> __m128i,
vectorized: unsafe fn(__m512i, __m512i) -> __m512i,
) {
let a = _mm512_set_epi64(
0xDCB4DB3657BF0B7D,
0x18DB0601068EDD9F,
0xB76B908233200DC5,
0xE478235FA8E22D5E,
0xAB05CFFA2621154C,
0x1171B47A186174C9,
0x8C6B6C0E7595CEC9,
0xBE3E7D4934E961BD,
);
let b = _mm512_set_epi64(
0x672F6F105A94CEA7,
0x8298B8FFCA5F829C,
0xA3927047B3FB61D8,
0x978093862CDE7187,
0xB1927AB22F31D0EC,
0xA9A5DA619BE4D7AF,
0xCA2590F56884FDC6,
0x19BE9F660038BDB5,
);
let a_decomp = transmute::<_, [__m128i; 4]>(a);
let b_decomp = transmute::<_, [__m128i; 4]>(b);
let r = vectorized(a, b);
let e_decomp = [
linear(a_decomp[0], b_decomp[0]),
linear(a_decomp[1], b_decomp[1]),
linear(a_decomp[2], b_decomp[2]),
linear(a_decomp[3], b_decomp[3]),
];
let e = transmute::<_, __m512i>(e_decomp);
assert_eq_m512i(r, e)
}
// this function tests one of the possible 4 instances
// with different inputs across lanes for the 256-bit version
#[target_feature(enable = "vpclmulqdq")]
unsafe fn verify_256_helper(
linear: unsafe fn(__m128i, __m128i) -> __m128i,
vectorized: unsafe fn(__m256i, __m256i) -> __m256i,
) {
let a = _mm256_set_epi64x(
0xDCB4DB3657BF0B7D,
0x18DB0601068EDD9F,
0xB76B908233200DC5,
0xE478235FA8E22D5E,
);
let b = _mm256_set_epi64x(
0x672F6F105A94CEA7,
0x8298B8FFCA5F829C,
0xA3927047B3FB61D8,
0x978093862CDE7187,
);
let a_decomp = transmute::<_, [__m128i; 2]>(a);
let b_decomp = transmute::<_, [__m128i; 2]>(b);
let r = vectorized(a, b);
let e_decomp = [linear(a_decomp[0], b_decomp[0]), linear(a_decomp[1], b_decomp[1])];
let e = transmute::<_, __m256i>(e_decomp);
assert_eq_m256i(r, e)
}
#[target_feature(enable = "vpclmulqdq,avx512f")]
unsafe fn test_mm512_clmulepi64_epi128() {
verify_kat_pclmul!(_mm512_broadcast_i32x4, _mm512_clmulepi64_epi128, assert_eq_m512i);
verify_512_helper(
|a, b| _mm_clmulepi64_si128::<0x00>(a, b),
|a, b| _mm512_clmulepi64_epi128::<0x00>(a, b),
);
verify_512_helper(
|a, b| _mm_clmulepi64_si128::<0x01>(a, b),
|a, b| _mm512_clmulepi64_epi128::<0x01>(a, b),
);
verify_512_helper(
|a, b| _mm_clmulepi64_si128::<0x10>(a, b),
|a, b| _mm512_clmulepi64_epi128::<0x10>(a, b),
);
verify_512_helper(
|a, b| _mm_clmulepi64_si128::<0x11>(a, b),
|a, b| _mm512_clmulepi64_epi128::<0x11>(a, b),
);
}
#[target_feature(enable = "vpclmulqdq")]
unsafe fn test_mm256_clmulepi64_epi128() {
verify_kat_pclmul!(_mm256_broadcastsi128_si256, _mm256_clmulepi64_epi128, assert_eq_m256i);
verify_256_helper(
|a, b| _mm_clmulepi64_si128::<0x00>(a, b),
|a, b| _mm256_clmulepi64_epi128::<0x00>(a, b),
);
verify_256_helper(
|a, b| _mm_clmulepi64_si128::<0x01>(a, b),
|a, b| _mm256_clmulepi64_epi128::<0x01>(a, b),
);
verify_256_helper(
|a, b| _mm_clmulepi64_si128::<0x10>(a, b),
|a, b| _mm256_clmulepi64_epi128::<0x10>(a, b),
);
verify_256_helper(
|a, b| _mm_clmulepi64_si128::<0x11>(a, b),
|a, b| _mm256_clmulepi64_epi128::<0x11>(a, b),
);
}
#[track_caller]
#[target_feature(enable = "avx512f")]
unsafe fn assert_eq_m512i(a: __m512i, b: __m512i) {
assert_eq!(transmute::<_, [u64; 8]>(a), transmute::<_, [u64; 8]>(b))
}
#[track_caller]
#[target_feature(enable = "avx")]
unsafe fn assert_eq_m256i(a: __m256i, b: __m256i) {
assert_eq!(transmute::<_, [u64; 4]>(a), transmute::<_, [u64; 4]>(b))
}

View File

@ -45,11 +45,11 @@ $DIR/doctest.rs:
LL| 1|//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() {
LL| 1|//! println!("{:?}", res);
LL| 1|//! }
^0
^0
LL| 1|//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() {
LL| 1|//! res = Ok(1);
LL| 1|//! }
^0
^0
LL| 1|//! res = Ok(0);
LL| |//! }
LL| |//! // need to be explicit because rustdoc cant infer the return type

View File

@ -1,5 +1,5 @@
Function name: abort::main
Raw bytes (89): 0x[01, 01, 0a, 01, 27, 05, 09, 03, 0d, 22, 11, 03, 0d, 03, 0d, 22, 15, 03, 0d, 03, 0d, 05, 09, 0d, 01, 0d, 01, 01, 1b, 03, 02, 0b, 00, 18, 22, 01, 0c, 00, 19, 11, 00, 1a, 02, 0a, 0e, 02, 0a, 00, 0b, 22, 02, 0c, 00, 19, 15, 00, 1a, 00, 31, 1a, 00, 31, 00, 32, 22, 04, 0c, 00, 19, 05, 00, 1a, 00, 31, 09, 00, 31, 00, 32, 27, 01, 09, 00, 17, 0d, 02, 05, 01, 02]
Raw bytes (89): 0x[01, 01, 0a, 01, 27, 05, 09, 03, 0d, 22, 11, 03, 0d, 03, 0d, 22, 15, 03, 0d, 03, 0d, 05, 09, 0d, 01, 0d, 01, 01, 1b, 03, 02, 0b, 00, 18, 22, 01, 0c, 00, 19, 11, 00, 1a, 02, 0a, 0e, 02, 09, 00, 0a, 22, 02, 0c, 00, 19, 15, 00, 1a, 00, 31, 1a, 00, 30, 00, 31, 22, 04, 0c, 00, 19, 05, 00, 1a, 00, 31, 09, 00, 30, 00, 31, 27, 01, 09, 00, 17, 0d, 02, 05, 01, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 10
@ -20,17 +20,17 @@ Number of file 0 mappings: 13
- Code(Expression(8, Sub)) at (prev + 1, 12) to (start + 0, 25)
= ((c0 + (c1 + c2)) - c3)
- Code(Counter(4)) at (prev + 0, 26) to (start + 2, 10)
- Code(Expression(3, Sub)) at (prev + 2, 10) to (start + 0, 11)
- Code(Expression(3, Sub)) at (prev + 2, 9) to (start + 0, 10)
= (((c0 + (c1 + c2)) - c3) - c4)
- Code(Expression(8, Sub)) at (prev + 2, 12) to (start + 0, 25)
= ((c0 + (c1 + c2)) - c3)
- Code(Counter(5)) at (prev + 0, 26) to (start + 0, 49)
- Code(Expression(6, Sub)) at (prev + 0, 49) to (start + 0, 50)
- Code(Expression(6, Sub)) at (prev + 0, 48) to (start + 0, 49)
= (((c0 + (c1 + c2)) - c3) - c5)
- Code(Expression(8, Sub)) at (prev + 4, 12) to (start + 0, 25)
= ((c0 + (c1 + c2)) - c3)
- Code(Counter(1)) at (prev + 0, 26) to (start + 0, 49)
- Code(Counter(2)) at (prev + 0, 49) to (start + 0, 50)
- Code(Counter(2)) at (prev + 0, 48) to (start + 0, 49)
- Code(Expression(9, Add)) at (prev + 1, 9) to (start + 0, 23)
= (c1 + c2)
- Code(Counter(3)) at (prev + 2, 5) to (start + 1, 2)

View File

@ -18,12 +18,12 @@
LL| 6| }
LL| | // See discussion (below the `Notes` section) on coverage results for the closing brace.
LL| 10| if countdown < 5 { might_abort(false); } // Counts for different regions on one line.
^4 ^6
^4 ^6
LL| | // For the following example, the closing brace is the last character on the line.
LL| | // This shows the character after the closing brace is highlighted, even if that next
LL| | // character is a newline.
LL| 10| if countdown < 5 { might_abort(false); }
^4 ^6
^4 ^6
LL| 10| countdown -= 1;
LL| | }
LL| 1| Ok(())

View File

@ -1,5 +1,5 @@
Function name: assert::main
Raw bytes (65): 0x[01, 01, 08, 01, 1b, 05, 1f, 09, 0d, 03, 11, 16, 05, 03, 11, 05, 1f, 09, 0d, 09, 01, 09, 01, 01, 1b, 03, 02, 0b, 00, 18, 16, 01, 0c, 00, 1a, 05, 00, 1b, 02, 0a, 12, 02, 13, 00, 20, 09, 00, 21, 02, 0a, 0d, 02, 0a, 00, 0b, 1b, 01, 09, 00, 17, 11, 02, 05, 01, 02]
Raw bytes (65): 0x[01, 01, 08, 01, 1b, 05, 1f, 09, 0d, 03, 11, 16, 05, 03, 11, 05, 1f, 09, 0d, 09, 01, 09, 01, 01, 1b, 03, 02, 0b, 00, 18, 16, 01, 0c, 00, 1a, 05, 00, 1b, 02, 0a, 12, 02, 13, 00, 20, 09, 00, 21, 02, 0a, 0d, 02, 09, 00, 0a, 1b, 01, 09, 00, 17, 11, 02, 05, 01, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 8
@ -21,7 +21,7 @@ Number of file 0 mappings: 9
- Code(Expression(4, Sub)) at (prev + 2, 19) to (start + 0, 32)
= (((c0 + (c1 + (c2 + c3))) - c4) - c1)
- Code(Counter(2)) at (prev + 0, 33) to (start + 2, 10)
- Code(Counter(3)) at (prev + 2, 10) to (start + 0, 11)
- Code(Counter(3)) at (prev + 2, 9) to (start + 0, 10)
- Code(Expression(6, Add)) at (prev + 1, 9) to (start + 0, 23)
= (c1 + (c2 + c3))
- Code(Counter(4)) at (prev + 2, 5) to (start + 1, 2)

View File

@ -8,14 +8,14 @@ Number of file 0 mappings: 1
Highest counter ID seen: c0
Function name: async2::async_func::{closure#0}
Raw bytes (24): 0x[01, 01, 00, 04, 01, 10, 17, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 06, 00, 07, 01, 01, 01, 00, 02]
Raw bytes (24): 0x[01, 01, 00, 04, 01, 10, 17, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 05, 00, 06, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 16, 23) to (start + 3, 9)
- Code(Counter(1)) at (prev + 3, 10) to (start + 2, 6)
- Code(Zero) at (prev + 2, 6) to (start + 0, 7)
- Code(Zero) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1
@ -47,14 +47,14 @@ Number of file 0 mappings: 1
Highest counter ID seen: c0
Function name: async2::non_async_func
Raw bytes (24): 0x[01, 01, 00, 04, 01, 08, 01, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 06, 00, 07, 01, 01, 01, 00, 02]
Raw bytes (24): 0x[01, 01, 00, 04, 01, 08, 01, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 05, 00, 06, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 8, 1) to (start + 3, 9)
- Code(Counter(1)) at (prev + 3, 10) to (start + 2, 6)
- Code(Zero) at (prev + 2, 6) to (start + 0, 7)
- Code(Zero) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1

View File

@ -11,7 +11,7 @@
LL| 1| if b {
LL| 1| println!("non_async_func println in block");
LL| 1| }
^0
^0
LL| 1|}
LL| |
LL| 1|async fn async_func() {
@ -20,7 +20,7 @@
LL| 1| if b {
LL| 1| println!("async_func println in block");
LL| 1| }
^0
^0
LL| 1|}
LL| |
LL| 1|async fn async_func_just_println() {

View File

@ -25,7 +25,7 @@ Number of file 0 mappings: 8
Highest counter ID seen: c4
Function name: if::branch_not
Raw bytes (116): 0x[01, 01, 07, 05, 09, 05, 0d, 05, 0d, 05, 11, 05, 11, 05, 15, 05, 15, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, 05, 01, 08, 00, 0a, 20, 0a, 0d, 00, 08, 00, 0a, 0a, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 05, 01, 08, 00, 0b, 20, 11, 12, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 12, 02, 06, 00, 07, 05, 01, 08, 00, 0c, 20, 1a, 15, 00, 08, 00, 0c, 1a, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 05, 01, 01, 00, 02]
Raw bytes (116): 0x[01, 01, 07, 05, 09, 05, 0d, 05, 0d, 05, 11, 05, 11, 05, 15, 05, 15, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 05, 00, 06, 05, 01, 08, 00, 0a, 20, 0a, 0d, 00, 08, 00, 0a, 0a, 00, 0b, 02, 06, 0d, 02, 05, 00, 06, 05, 01, 08, 00, 0b, 20, 11, 12, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 12, 02, 05, 00, 06, 05, 01, 08, 00, 0c, 20, 1a, 15, 00, 08, 00, 0c, 1a, 00, 0d, 02, 06, 15, 02, 05, 00, 06, 05, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 7
@ -43,7 +43,7 @@ Number of file 0 mappings: 18
true = c2
false = (c1 - c2)
- Code(Counter(2)) at (prev + 1, 9) to (start + 0, 17)
- Code(Expression(0, Sub)) at (prev + 1, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 6)
= (c1 - c2)
- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 10)
- Branch { true: Expression(2, Sub), false: Counter(3) } at (prev + 0, 8) to (start + 0, 10)
@ -51,13 +51,13 @@ Number of file 0 mappings: 18
false = c3
- Code(Expression(2, Sub)) at (prev + 0, 11) to (start + 2, 6)
= (c1 - c3)
- Code(Counter(3)) at (prev + 2, 6) to (start + 0, 7)
- Code(Counter(3)) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 11)
- Branch { true: Counter(4), false: Expression(4, Sub) } at (prev + 0, 8) to (start + 0, 11)
true = c4
false = (c1 - c4)
- Code(Counter(4)) at (prev + 0, 12) to (start + 2, 6)
- Code(Expression(4, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(4, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c1 - c4)
- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 12)
- Branch { true: Expression(6, Sub), false: Counter(5) } at (prev + 0, 8) to (start + 0, 12)
@ -65,12 +65,12 @@ Number of file 0 mappings: 18
false = c5
- Code(Expression(6, Sub)) at (prev + 0, 13) to (start + 2, 6)
= (c1 - c5)
- Code(Counter(5)) at (prev + 2, 6) to (start + 0, 7)
- Code(Counter(5)) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c5
Function name: if::branch_not_as
Raw bytes (90): 0x[01, 01, 05, 05, 09, 05, 0d, 05, 0d, 05, 11, 05, 11, 0e, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 20, 02, 09, 00, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 05, 01, 08, 00, 15, 20, 0d, 0a, 00, 08, 00, 15, 0d, 00, 16, 02, 06, 0a, 02, 06, 00, 07, 05, 01, 08, 00, 16, 20, 12, 11, 00, 08, 00, 16, 12, 00, 17, 02, 06, 11, 02, 06, 00, 07, 05, 01, 01, 00, 02]
Raw bytes (90): 0x[01, 01, 05, 05, 09, 05, 0d, 05, 0d, 05, 11, 05, 11, 0e, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 20, 02, 09, 00, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 05, 00, 06, 05, 01, 08, 00, 15, 20, 0d, 0a, 00, 08, 00, 15, 0d, 00, 16, 02, 06, 0a, 02, 05, 00, 06, 05, 01, 08, 00, 16, 20, 12, 11, 00, 08, 00, 16, 12, 00, 17, 02, 06, 11, 02, 05, 00, 06, 05, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 5
@ -87,13 +87,13 @@ Number of file 0 mappings: 14
false = c2
- Code(Expression(0, Sub)) at (prev + 0, 21) to (start + 2, 6)
= (c1 - c2)
- Code(Counter(2)) at (prev + 2, 6) to (start + 0, 7)
- Code(Counter(2)) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 21)
- Branch { true: Counter(3), false: Expression(2, Sub) } at (prev + 0, 8) to (start + 0, 21)
true = c3
false = (c1 - c3)
- Code(Counter(3)) at (prev + 0, 22) to (start + 2, 6)
- Code(Expression(2, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(2, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c1 - c3)
- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 22)
- Branch { true: Expression(4, Sub), false: Counter(4) } at (prev + 0, 8) to (start + 0, 22)
@ -101,7 +101,7 @@ Number of file 0 mappings: 14
false = c4
- Code(Expression(4, Sub)) at (prev + 0, 23) to (start + 2, 6)
= (c1 - c4)
- Code(Counter(4)) at (prev + 2, 6) to (start + 0, 7)
- Code(Counter(4)) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c4

View File

@ -30,7 +30,7 @@
------------------
LL| 2| say("not not a");
LL| 2| }
^1
^1
LL| 3| if !!!a {
------------------
| Branch (LL:8): [True: 1, False: 2]
@ -54,7 +54,7 @@
------------------
LL| 2| say("not not (a as bool)");
LL| 2| }
^1
^1
LL| 3| if !!!(a as bool) {
------------------
| Branch (LL:8): [True: 1, False: 2]

View File

@ -1,5 +1,5 @@
Function name: closure::main
Raw bytes (126): 0x[01, 01, 01, 01, 05, 18, 01, 09, 01, 0f, 0d, 01, 16, 0e, 06, 0a, 01, 10, 05, 13, 0d, 01, 1a, 0e, 06, 0a, 01, 10, 05, 0c, 16, 01, 16, 05, 0d, 18, 01, 19, 09, 01, 1e, 01, 04, 09, 00, 29, 01, 01, 09, 00, 2d, 01, 01, 09, 00, 24, 01, 05, 09, 00, 24, 01, 02, 09, 00, 21, 01, 04, 09, 00, 21, 01, 04, 09, 00, 28, 01, 09, 09, 00, 32, 01, 04, 09, 00, 33, 01, 07, 09, 00, 4b, 01, 08, 09, 00, 48, 01, 0a, 09, 00, 47, 01, 08, 09, 00, 44, 01, 0a, 08, 00, 10, 05, 00, 11, 04, 06, 02, 04, 06, 00, 07, 01, 01, 05, 03, 02]
Raw bytes (126): 0x[01, 01, 01, 01, 05, 18, 01, 09, 01, 0f, 0d, 01, 16, 0e, 06, 0a, 01, 10, 05, 13, 0d, 01, 1a, 0e, 06, 0a, 01, 10, 05, 0c, 16, 01, 16, 05, 0d, 18, 01, 19, 09, 01, 1e, 01, 04, 09, 00, 29, 01, 01, 09, 00, 2d, 01, 01, 09, 00, 24, 01, 05, 09, 00, 24, 01, 02, 09, 00, 21, 01, 04, 09, 00, 21, 01, 04, 09, 00, 28, 01, 09, 09, 00, 32, 01, 04, 09, 00, 33, 01, 07, 09, 00, 4b, 01, 08, 09, 00, 48, 01, 0a, 09, 00, 47, 01, 08, 09, 00, 44, 01, 0a, 08, 00, 10, 05, 00, 11, 04, 06, 02, 04, 05, 00, 06, 01, 01, 05, 03, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -27,13 +27,13 @@ Number of file 0 mappings: 24
- Code(Counter(0)) at (prev + 8, 9) to (start + 0, 68)
- Code(Counter(0)) at (prev + 10, 8) to (start + 0, 16)
- Code(Counter(1)) at (prev + 0, 17) to (start + 4, 6)
- Code(Expression(0, Sub)) at (prev + 4, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 4, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 5) to (start + 3, 2)
Highest counter ID seen: c1
Function name: closure::main::{closure#0}
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 28, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 01, 01, 09, 01, 06]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 28, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 09, 00, 0a, 01, 01, 09, 01, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -41,7 +41,7 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 40, 5) to (start + 2, 20)
- Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10)
- Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11)
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 10)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 9) to (start + 1, 6)
Highest counter ID seen: c1
@ -83,17 +83,16 @@ Number of file 0 mappings: 1
Highest counter ID seen: (none)
Function name: closure::main::{closure#14}
Raw bytes (27): 0x[01, 01, 01, 01, 05, 04, 01, b3, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 01, 0d, 00, 0e]
Raw bytes (22): 0x[01, 01, 01, 01, 05, 03, 01, b3, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
Number of file 0 mappings: 4
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 179, 13) to (start + 2, 27)
- Code(Counter(1)) at (prev + 2, 30) to (start + 0, 37)
- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 14)
Highest counter ID seen: c1
Function name: closure::main::{closure#15}
@ -113,17 +112,16 @@ Number of file 0 mappings: 6
Highest counter ID seen: c1
Function name: closure::main::{closure#16}
Raw bytes (27): 0x[01, 01, 01, 01, 05, 04, 01, c5, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 01, 0d, 00, 0e]
Raw bytes (22): 0x[01, 01, 01, 01, 05, 03, 01, c5, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
Number of file 0 mappings: 4
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 197, 13) to (start + 2, 27)
- Code(Counter(1)) at (prev + 2, 30) to (start + 0, 37)
- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 14)
Highest counter ID seen: c1
Function name: closure::main::{closure#17}
@ -143,19 +141,19 @@ Number of file 0 mappings: 6
Highest counter ID seen: c1
Function name: closure::main::{closure#18} (unused)
Raw bytes (24): 0x[01, 01, 00, 04, 00, 19, 0d, 02, 1c, 00, 02, 1d, 02, 12, 00, 02, 12, 00, 13, 00, 01, 11, 01, 0e]
Raw bytes (24): 0x[01, 01, 00, 04, 00, 19, 0d, 02, 1c, 00, 02, 1d, 02, 12, 00, 02, 11, 00, 12, 00, 01, 11, 01, 0e]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Zero) at (prev + 25, 13) to (start + 2, 28)
- Code(Zero) at (prev + 2, 29) to (start + 2, 18)
- Code(Zero) at (prev + 2, 18) to (start + 0, 19)
- Code(Zero) at (prev + 2, 17) to (start + 0, 18)
- Code(Zero) at (prev + 1, 17) to (start + 1, 14)
Highest counter ID seen: (none)
Function name: closure::main::{closure#19}
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 43, 0d, 02, 1c, 05, 02, 1d, 02, 12, 02, 02, 12, 00, 13, 01, 01, 11, 01, 0e]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 43, 0d, 02, 1c, 05, 02, 1d, 02, 12, 02, 02, 11, 00, 12, 01, 01, 11, 01, 0e]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -163,13 +161,13 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 67, 13) to (start + 2, 28)
- Code(Counter(1)) at (prev + 2, 29) to (start + 2, 18)
- Code(Expression(0, Sub)) at (prev + 2, 18) to (start + 0, 19)
- Code(Expression(0, Sub)) at (prev + 2, 17) to (start + 0, 18)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 17) to (start + 1, 14)
Highest counter ID seen: c1
Function name: closure::main::{closure#1}
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 52, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 01, 01, 09, 01, 06]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 52, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 09, 00, 0a, 01, 01, 09, 01, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -177,13 +175,13 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 82, 5) to (start + 2, 20)
- Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10)
- Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11)
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 10)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 9) to (start + 1, 6)
Highest counter ID seen: c1
Function name: closure::main::{closure#2}
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 68, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 01, 01, 09, 01, 06]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 68, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 09, 00, 0a, 01, 01, 09, 01, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -191,20 +189,20 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 104, 5) to (start + 2, 20)
- Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10)
- Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11)
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 10)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 9) to (start + 1, 6)
Highest counter ID seen: c1
Function name: closure::main::{closure#3} (unused)
Raw bytes (25): 0x[01, 01, 00, 04, 00, 81, 01, 05, 01, 14, 00, 01, 15, 02, 0a, 00, 02, 0a, 00, 0b, 00, 01, 09, 01, 06]
Raw bytes (25): 0x[01, 01, 00, 04, 00, 81, 01, 05, 01, 14, 00, 01, 15, 02, 0a, 00, 02, 09, 00, 0a, 00, 01, 09, 01, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Zero) at (prev + 129, 5) to (start + 1, 20)
- Code(Zero) at (prev + 1, 21) to (start + 2, 10)
- Code(Zero) at (prev + 2, 10) to (start + 0, 11)
- Code(Zero) at (prev + 2, 9) to (start + 0, 10)
- Code(Zero) at (prev + 1, 9) to (start + 1, 6)
Highest counter ID seen: (none)

View File

@ -183,7 +183,7 @@
LL| 0| println!(
LL| 0| "not called: {}",
LL| 0| if is_true { "check" } else { "me" }
LL| 0| )
LL| | )
LL| | ;
LL| |
LL| 1| let short_used_not_covered_closure_line_break_block_embedded_branch =
@ -202,7 +202,7 @@
LL| 1| "not called: {}",
LL| 1| if is_true { "check" } else { "me" }
^0
LL| 1| )
LL| | )
LL| | ;
LL| |
LL| 1| let short_used_covered_closure_line_break_block_embedded_branch =

View File

@ -1,5 +1,5 @@
Function name: closure_bug::main
Raw bytes (97): 0x[01, 01, 04, 01, 05, 01, 09, 01, 0d, 01, 11, 11, 01, 07, 01, 03, 0a, 01, 09, 05, 01, 0e, 05, 01, 0f, 00, 17, 02, 00, 17, 00, 18, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 09, 01, 0f, 00, 17, 06, 00, 17, 00, 18, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 0d, 01, 0f, 00, 17, 0a, 00, 17, 00, 18, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 11, 01, 0f, 00, 17, 0e, 00, 17, 00, 18, 01, 01, 01, 00, 02]
Raw bytes (97): 0x[01, 01, 04, 01, 05, 01, 09, 01, 0d, 01, 11, 11, 01, 07, 01, 03, 0a, 01, 09, 05, 01, 0e, 05, 01, 0f, 00, 17, 02, 00, 16, 00, 17, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 09, 01, 0f, 00, 17, 06, 00, 16, 00, 17, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 0d, 01, 0f, 00, 17, 0a, 00, 16, 00, 17, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 11, 01, 0f, 00, 17, 0e, 00, 16, 00, 17, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 4
@ -11,22 +11,22 @@ Number of file 0 mappings: 17
- Code(Counter(0)) at (prev + 7, 1) to (start + 3, 10)
- Code(Counter(0)) at (prev + 9, 5) to (start + 1, 14)
- Code(Counter(1)) at (prev + 1, 15) to (start + 0, 23)
- Code(Expression(0, Sub)) at (prev + 0, 23) to (start + 0, 24)
- Code(Expression(0, Sub)) at (prev + 0, 22) to (start + 0, 23)
= (c0 - c1)
- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10)
- Code(Counter(0)) at (prev + 6, 5) to (start + 1, 14)
- Code(Counter(2)) at (prev + 1, 15) to (start + 0, 23)
- Code(Expression(1, Sub)) at (prev + 0, 23) to (start + 0, 24)
- Code(Expression(1, Sub)) at (prev + 0, 22) to (start + 0, 23)
= (c0 - c2)
- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10)
- Code(Counter(0)) at (prev + 6, 5) to (start + 1, 14)
- Code(Counter(3)) at (prev + 1, 15) to (start + 0, 23)
- Code(Expression(2, Sub)) at (prev + 0, 23) to (start + 0, 24)
- Code(Expression(2, Sub)) at (prev + 0, 22) to (start + 0, 23)
= (c0 - c3)
- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10)
- Code(Counter(0)) at (prev + 6, 5) to (start + 1, 14)
- Code(Counter(4)) at (prev + 1, 15) to (start + 0, 23)
- Code(Expression(3, Sub)) at (prev + 0, 23) to (start + 0, 24)
- Code(Expression(3, Sub)) at (prev + 0, 22) to (start + 0, 23)
= (c0 - c4)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c4

View File

@ -16,7 +16,7 @@
LL| |
LL| 1| a();
LL| 1| if truthy { a(); }
^0
^0
LL| |
LL| 1| let b
LL| | =
@ -27,7 +27,7 @@
LL| |
LL| 1| b();
LL| 1| if truthy { b(); }
^0
^0
LL| |
LL| 1| let c
LL| | =
@ -38,7 +38,7 @@
LL| |
LL| 1| c();
LL| 1| if truthy { c(); }
^0
^0
LL| |
LL| 1| let d
LL| | =
@ -49,6 +49,6 @@
LL| |
LL| 1| d();
LL| 1| if truthy { d(); }
^0
^0
LL| 1|}

View File

@ -1,5 +1,5 @@
Function name: conditions::main
Raw bytes (799): 0x[01, 01, 94, 01, 09, 2b, 2f, 41, 33, 3d, 35, 39, 01, 09, 0d, 35, 1e, 39, 0d, 35, 33, 3d, 35, 39, 2f, 41, 33, 3d, 35, 39, ce, 04, 0d, 01, 09, 03, 49, 62, 31, 03, 49, 5e, 4d, 62, 31, 03, 49, 5a, 51, 5e, 4d, 62, 31, 03, 49, 87, 01, 55, 4d, 51, 83, 01, 59, 87, 01, 55, 4d, 51, 49, 7f, 83, 01, 59, 87, 01, 55, 4d, 51, 5d, 65, ae, 01, 2d, 5d, 65, aa, 01, 69, ae, 01, 2d, 5d, 65, a6, 01, 6d, aa, 01, 69, ae, 01, 2d, 5d, 65, f3, 02, 71, 69, 6d, ef, 02, 75, f3, 02, 71, 69, 6d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, da, 02, 81, 01, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, d6, 02, 85, 01, da, 02, 81, 01, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, 8f, 04, 89, 01, 81, 01, 85, 01, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, 11, af, 04, b3, 04, 21, b7, 04, 1d, 15, 19, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, fa, 03, 15, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, f6, 03, 19, fa, 03, 15, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, b7, 04, 1d, 15, 19, b3, 04, 21, b7, 04, 1d, 15, 19, ab, 04, bb, 04, 11, af, 04, b3, 04, 21, b7, 04, 1d, 15, 19, bf, 04, ca, 04, c3, 04, 31, c7, 04, 2d, 25, 29, ce, 04, 0d, 01, 09, 44, 01, 03, 01, 02, 0c, 05, 02, 0d, 02, 06, 00, 02, 06, 00, 07, 03, 03, 09, 00, 0a, 01, 00, 10, 00, 1d, 09, 01, 09, 01, 0a, ce, 04, 02, 0f, 00, 1c, 0d, 01, 0c, 00, 19, 1e, 00, 1d, 00, 2a, 1a, 00, 2e, 00, 3c, 2f, 00, 3d, 02, 0a, 41, 02, 0a, 00, 0b, 2b, 01, 09, 01, 12, ca, 04, 03, 09, 00, 0f, 03, 03, 09, 01, 0c, 45, 01, 0d, 02, 06, 00, 02, 06, 00, 07, 03, 02, 08, 00, 15, 49, 00, 16, 02, 06, 62, 02, 0f, 00, 1c, 5e, 01, 0c, 00, 19, 5a, 00, 1d, 00, 2a, 56, 00, 2e, 00, 3c, 83, 01, 00, 3d, 02, 0a, 59, 02, 0a, 00, 0b, 7f, 01, 09, 00, 17, 31, 02, 09, 00, 0f, 7b, 03, 08, 00, 0c, 5d, 01, 0d, 01, 10, 61, 01, 11, 02, 0a, 00, 02, 0a, 00, 0b, 5d, 02, 0c, 00, 19, 65, 00, 1a, 02, 0a, ae, 01, 04, 11, 00, 1e, aa, 01, 01, 10, 00, 1d, a6, 01, 00, 21, 00, 2e, a2, 01, 00, 32, 00, 40, ef, 02, 00, 41, 02, 0e, 75, 02, 0e, 00, 0f, eb, 02, 01, 0d, 00, 1b, 2d, 02, 0d, 00, 13, 00, 02, 06, 00, 07, e3, 02, 02, 09, 01, 0c, 79, 01, 0d, 02, 06, 00, 02, 06, 00, 07, 83, 04, 02, 09, 00, 0a, e3, 02, 00, 10, 00, 1d, 7d, 00, 1e, 02, 06, de, 02, 02, 0f, 00, 1c, da, 02, 01, 0c, 00, 19, d6, 02, 00, 1d, 00, 2a, d2, 02, 00, 2e, 00, 3c, 8b, 04, 00, 3d, 02, 0a, 8d, 01, 02, 0a, 00, 0b, 87, 04, 01, 09, 00, 17, 29, 02, 0d, 02, 0f, ab, 04, 05, 09, 00, 0a, 83, 04, 00, 10, 00, 1d, 11, 00, 1e, 02, 06, fe, 03, 02, 0f, 00, 1c, fa, 03, 01, 0c, 00, 19, f6, 03, 00, 1d, 00, 2a, f2, 03, 00, 2e, 00, 3c, b3, 04, 00, 3d, 02, 0a, 21, 02, 0a, 00, 0b, af, 04, 01, 09, 00, 17, 25, 02, 09, 00, 0f, a7, 04, 02, 01, 00, 02]
Raw bytes (799): 0x[01, 01, 94, 01, 09, 2b, 2f, 41, 33, 3d, 35, 39, 01, 09, 0d, 35, 1e, 39, 0d, 35, 33, 3d, 35, 39, 2f, 41, 33, 3d, 35, 39, ce, 04, 0d, 01, 09, 03, 49, 62, 31, 03, 49, 5e, 4d, 62, 31, 03, 49, 5a, 51, 5e, 4d, 62, 31, 03, 49, 87, 01, 55, 4d, 51, 83, 01, 59, 87, 01, 55, 4d, 51, 49, 7f, 83, 01, 59, 87, 01, 55, 4d, 51, 5d, 65, ae, 01, 2d, 5d, 65, aa, 01, 69, ae, 01, 2d, 5d, 65, a6, 01, 6d, aa, 01, 69, ae, 01, 2d, 5d, 65, f3, 02, 71, 69, 6d, ef, 02, 75, f3, 02, 71, 69, 6d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, da, 02, 81, 01, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, d6, 02, 85, 01, da, 02, 81, 01, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, 8f, 04, 89, 01, 81, 01, 85, 01, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, 11, af, 04, b3, 04, 21, b7, 04, 1d, 15, 19, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, fa, 03, 15, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, f6, 03, 19, fa, 03, 15, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, b7, 04, 1d, 15, 19, b3, 04, 21, b7, 04, 1d, 15, 19, ab, 04, bb, 04, 11, af, 04, b3, 04, 21, b7, 04, 1d, 15, 19, bf, 04, ca, 04, c3, 04, 31, c7, 04, 2d, 25, 29, ce, 04, 0d, 01, 09, 44, 01, 03, 01, 02, 0c, 05, 02, 0d, 02, 06, 00, 02, 05, 00, 06, 03, 03, 09, 00, 0a, 01, 00, 10, 00, 1d, 09, 01, 09, 01, 0a, ce, 04, 02, 0f, 00, 1c, 0d, 01, 0c, 00, 19, 1e, 00, 1d, 00, 2a, 1a, 00, 2e, 00, 3c, 2f, 00, 3d, 02, 0a, 41, 02, 09, 00, 0a, 2b, 01, 09, 01, 12, ca, 04, 03, 09, 00, 0f, 03, 03, 09, 01, 0c, 45, 01, 0d, 02, 06, 00, 02, 05, 00, 06, 03, 02, 08, 00, 15, 49, 00, 16, 02, 06, 62, 02, 0f, 00, 1c, 5e, 01, 0c, 00, 19, 5a, 00, 1d, 00, 2a, 56, 00, 2e, 00, 3c, 83, 01, 00, 3d, 02, 0a, 59, 02, 09, 00, 0a, 7f, 01, 09, 00, 17, 31, 02, 09, 00, 0f, 7b, 03, 08, 00, 0c, 5d, 01, 0d, 01, 10, 61, 01, 11, 02, 0a, 00, 02, 09, 00, 0a, 5d, 02, 0c, 00, 19, 65, 00, 1a, 02, 0a, ae, 01, 04, 11, 00, 1e, aa, 01, 01, 10, 00, 1d, a6, 01, 00, 21, 00, 2e, a2, 01, 00, 32, 00, 40, ef, 02, 00, 41, 02, 0e, 75, 02, 0d, 00, 0e, eb, 02, 01, 0d, 00, 1b, 2d, 02, 0d, 00, 13, 00, 02, 05, 00, 06, e3, 02, 02, 09, 01, 0c, 79, 01, 0d, 02, 06, 00, 02, 05, 00, 06, 83, 04, 02, 09, 00, 0a, e3, 02, 00, 10, 00, 1d, 7d, 00, 1e, 02, 06, de, 02, 02, 0f, 00, 1c, da, 02, 01, 0c, 00, 19, d6, 02, 00, 1d, 00, 2a, d2, 02, 00, 2e, 00, 3c, 8b, 04, 00, 3d, 02, 0a, 8d, 01, 02, 09, 00, 0a, 87, 04, 01, 09, 00, 17, 29, 02, 0d, 02, 0f, ab, 04, 05, 09, 00, 0a, 83, 04, 00, 10, 00, 1d, 11, 00, 1e, 02, 06, fe, 03, 02, 0f, 00, 1c, fa, 03, 01, 0c, 00, 19, f6, 03, 00, 1d, 00, 2a, f2, 03, 00, 2e, 00, 3c, b3, 04, 00, 3d, 02, 0a, 21, 02, 09, 00, 0a, af, 04, 01, 09, 00, 17, 25, 02, 09, 00, 0f, a7, 04, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 148
@ -154,7 +154,7 @@ Number of expressions: 148
Number of file 0 mappings: 68
- Code(Counter(0)) at (prev + 3, 1) to (start + 2, 12)
- Code(Counter(1)) at (prev + 2, 13) to (start + 2, 6)
- Code(Zero) at (prev + 2, 6) to (start + 0, 7)
- Code(Zero) at (prev + 2, 5) to (start + 0, 6)
- Code(Expression(0, Add)) at (prev + 3, 9) to (start + 0, 10)
= (c2 + (((c13 + c14) + c15) + c16))
- Code(Counter(0)) at (prev + 0, 16) to (start + 0, 29)
@ -168,7 +168,7 @@ Number of file 0 mappings: 68
= ((c3 - c13) - c14)
- Code(Expression(11, Add)) at (prev + 0, 61) to (start + 2, 10)
= ((c13 + c14) + c15)
- Code(Counter(16)) at (prev + 2, 10) to (start + 0, 11)
- Code(Counter(16)) at (prev + 2, 9) to (start + 0, 10)
- Code(Expression(10, Add)) at (prev + 1, 9) to (start + 1, 18)
= (((c13 + c14) + c15) + c16)
- Code(Expression(146, Sub)) at (prev + 3, 9) to (start + 0, 15)
@ -176,7 +176,7 @@ Number of file 0 mappings: 68
- Code(Expression(0, Add)) at (prev + 3, 9) to (start + 1, 12)
= (c2 + (((c13 + c14) + c15) + c16))
- Code(Counter(17)) at (prev + 1, 13) to (start + 2, 6)
- Code(Zero) at (prev + 2, 6) to (start + 0, 7)
- Code(Zero) at (prev + 2, 5) to (start + 0, 6)
- Code(Expression(0, Add)) at (prev + 2, 8) to (start + 0, 21)
= (c2 + (((c13 + c14) + c15) + c16))
- Code(Counter(18)) at (prev + 0, 22) to (start + 2, 6)
@ -190,7 +190,7 @@ Number of file 0 mappings: 68
= (((((c2 + (((c13 + c14) + c15) + c16)) - c18) - c12) - c19) - c20)
- Code(Expression(32, Add)) at (prev + 0, 61) to (start + 2, 10)
= ((c19 + c20) + c21)
- Code(Counter(22)) at (prev + 2, 10) to (start + 0, 11)
- Code(Counter(22)) at (prev + 2, 9) to (start + 0, 10)
- Code(Expression(31, Add)) at (prev + 1, 9) to (start + 0, 23)
= (((c19 + c20) + c21) + c22)
- Code(Counter(12)) at (prev + 2, 9) to (start + 0, 15)
@ -198,7 +198,7 @@ Number of file 0 mappings: 68
= (c18 + (((c19 + c20) + c21) + c22))
- Code(Counter(23)) at (prev + 1, 13) to (start + 1, 16)
- Code(Counter(24)) at (prev + 1, 17) to (start + 2, 10)
- Code(Zero) at (prev + 2, 10) to (start + 0, 11)
- Code(Zero) at (prev + 2, 9) to (start + 0, 10)
- Code(Counter(23)) at (prev + 2, 12) to (start + 0, 25)
- Code(Counter(25)) at (prev + 0, 26) to (start + 2, 10)
- Code(Expression(43, Sub)) at (prev + 4, 17) to (start + 0, 30)
@ -211,15 +211,15 @@ Number of file 0 mappings: 68
= ((((c23 - c25) - c11) - c26) - c27)
- Code(Expression(91, Add)) at (prev + 0, 65) to (start + 2, 14)
= ((c26 + c27) + c28)
- Code(Counter(29)) at (prev + 2, 14) to (start + 0, 15)
- Code(Counter(29)) at (prev + 2, 13) to (start + 0, 14)
- Code(Expression(90, Add)) at (prev + 1, 13) to (start + 0, 27)
= (((c26 + c27) + c28) + c29)
- Code(Counter(11)) at (prev + 2, 13) to (start + 0, 19)
- Code(Zero) at (prev + 2, 6) to (start + 0, 7)
- Code(Zero) at (prev + 2, 5) to (start + 0, 6)
- Code(Expression(88, Add)) at (prev + 2, 9) to (start + 1, 12)
= ((c25 + (((c26 + c27) + c28) + c29)) + Zero)
- Code(Counter(30)) at (prev + 1, 13) to (start + 2, 6)
- Code(Zero) at (prev + 2, 6) to (start + 0, 7)
- Code(Zero) at (prev + 2, 5) to (start + 0, 6)
- Code(Expression(128, Add)) at (prev + 2, 9) to (start + 0, 10)
= (c31 + (((c32 + c33) + c34) + c35))
- Code(Expression(88, Add)) at (prev + 0, 16) to (start + 0, 29)
@ -235,7 +235,7 @@ Number of file 0 mappings: 68
= ((((((c25 + (((c26 + c27) + c28) + c29)) + Zero) - c31) - c10) - c32) - c33)
- Code(Expression(130, Add)) at (prev + 0, 61) to (start + 2, 10)
= ((c32 + c33) + c34)
- Code(Counter(35)) at (prev + 2, 10) to (start + 0, 11)
- Code(Counter(35)) at (prev + 2, 9) to (start + 0, 10)
- Code(Expression(129, Add)) at (prev + 1, 9) to (start + 0, 23)
= (((c32 + c33) + c34) + c35)
- Code(Counter(10)) at (prev + 2, 13) to (start + 2, 15)
@ -254,7 +254,7 @@ Number of file 0 mappings: 68
= (((((c31 + (((c32 + c33) + c34) + c35)) - c4) - c9) - c5) - c6)
- Code(Expression(140, Add)) at (prev + 0, 61) to (start + 2, 10)
= ((c5 + c6) + c7)
- Code(Counter(8)) at (prev + 2, 10) to (start + 0, 11)
- Code(Counter(8)) at (prev + 2, 9) to (start + 0, 10)
- Code(Expression(139, Add)) at (prev + 1, 9) to (start + 0, 23)
= (((c5 + c6) + c7) + c8)
- Code(Counter(9)) at (prev + 2, 9) to (start + 0, 15)

View File

@ -5,7 +5,7 @@
LL| 1| if true {
LL| 1| countdown = 10;
LL| 1| }
^0
^0
LL| |
LL| | const B: u32 = 100;
LL| 1| let x = if countdown > 7 {
@ -25,7 +25,7 @@
LL| 1| if true {
LL| 1| countdown = 10;
LL| 1| }
^0
^0
LL| |
LL| 1| if countdown > 7 {
LL| 1| countdown -= 4;
@ -44,7 +44,7 @@
LL| 1| if true {
LL| 1| countdown = 10;
LL| 1| }
^0
^0
LL| |
LL| 1| if countdown > 7 {
LL| 1| countdown -= 4;
@ -64,7 +64,7 @@
LL| 1| if true {
LL| 1| countdown = 1;
LL| 1| }
^0
^0
LL| |
LL| 1| let z = if countdown > 7 {
^0

View File

@ -1,5 +1,5 @@
Function name: dead_code::main
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 1b, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 1b, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 05, 00, 06, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -7,32 +7,32 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 27, 1) to (start + 7, 15)
- Code(Counter(1)) at (prev + 7, 16) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1
Function name: dead_code::unused_fn (unused)
Raw bytes (24): 0x[01, 01, 00, 04, 00, 0f, 01, 07, 0f, 00, 07, 10, 02, 06, 00, 02, 06, 00, 07, 00, 01, 01, 00, 02]
Raw bytes (24): 0x[01, 01, 00, 04, 00, 0f, 01, 07, 0f, 00, 07, 10, 02, 06, 00, 02, 05, 00, 06, 00, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Zero) at (prev + 15, 1) to (start + 7, 15)
- Code(Zero) at (prev + 7, 16) to (start + 2, 6)
- Code(Zero) at (prev + 2, 6) to (start + 0, 7)
- Code(Zero) at (prev + 2, 5) to (start + 0, 6)
- Code(Zero) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: (none)
Function name: dead_code::unused_pub_fn_not_in_library (unused)
Raw bytes (24): 0x[01, 01, 00, 04, 00, 03, 01, 07, 0f, 00, 07, 10, 02, 06, 00, 02, 06, 00, 07, 00, 01, 01, 00, 02]
Raw bytes (24): 0x[01, 01, 00, 04, 00, 03, 01, 07, 0f, 00, 07, 10, 02, 06, 00, 02, 05, 00, 06, 00, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Zero) at (prev + 3, 1) to (start + 7, 15)
- Code(Zero) at (prev + 7, 16) to (start + 2, 6)
- Code(Zero) at (prev + 2, 6) to (start + 0, 7)
- Code(Zero) at (prev + 2, 5) to (start + 0, 6)
- Code(Zero) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: (none)

View File

@ -34,6 +34,6 @@
LL| 1| if is_true {
LL| 1| countdown = 10;
LL| 1| }
^0
^0
LL| 1|}

View File

@ -1,5 +1,5 @@
Function name: if::main
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 04, 01, 12, 10, 05, 13, 05, 05, 06, 02, 05, 06, 00, 07, 01, 01, 01, 00, 02]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 04, 01, 12, 10, 05, 13, 05, 05, 06, 02, 05, 05, 00, 06, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -7,7 +7,7 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 4, 1) to (start + 18, 16)
- Code(Counter(1)) at (prev + 19, 5) to (start + 5, 6)
- Code(Expression(0, Sub)) at (prev + 5, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 5, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1

View File

@ -26,6 +26,6 @@
LL| 1| 10
LL| 1| ;
LL| 1| }
^0
^0
LL| 1|}

View File

@ -1,5 +1,5 @@
Function name: if_not::if_not
Raw bytes (60): 0x[01, 01, 03, 01, 05, 01, 09, 01, 0d, 0a, 01, 05, 01, 03, 0d, 02, 04, 05, 02, 06, 05, 02, 06, 00, 07, 01, 03, 09, 01, 0d, 06, 02, 05, 02, 06, 09, 02, 06, 00, 07, 01, 03, 09, 01, 0d, 0a, 02, 05, 02, 06, 0d, 02, 0c, 02, 06, 01, 03, 01, 00, 02]
Raw bytes (60): 0x[01, 01, 03, 01, 05, 01, 09, 01, 0d, 0a, 01, 05, 01, 03, 0d, 02, 04, 05, 02, 06, 05, 02, 05, 00, 06, 01, 03, 09, 01, 0d, 06, 02, 05, 02, 06, 09, 02, 05, 00, 06, 01, 03, 09, 01, 0d, 0a, 02, 05, 02, 06, 0d, 02, 0c, 02, 06, 01, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 3
@ -10,11 +10,11 @@ Number of file 0 mappings: 10
- Code(Counter(0)) at (prev + 5, 1) to (start + 3, 13)
- Code(Expression(0, Sub)) at (prev + 4, 5) to (start + 2, 6)
= (c0 - c1)
- Code(Counter(1)) at (prev + 2, 6) to (start + 0, 7)
- Code(Counter(1)) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(0)) at (prev + 3, 9) to (start + 1, 13)
- Code(Expression(1, Sub)) at (prev + 2, 5) to (start + 2, 6)
= (c0 - c2)
- Code(Counter(2)) at (prev + 2, 6) to (start + 0, 7)
- Code(Counter(2)) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(0)) at (prev + 3, 9) to (start + 1, 13)
- Code(Expression(2, Sub)) at (prev + 2, 5) to (start + 2, 6)
= (c0 - c3)

View File

@ -17,7 +17,7 @@ Number of file 0 mappings: 1
Highest counter ID seen: c0
Function name: inner_items::main
Raw bytes (43): 0x[01, 01, 02, 01, 05, 01, 09, 07, 01, 03, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 01, 24, 08, 00, 0f, 09, 00, 10, 02, 06, 06, 02, 06, 00, 07, 01, 02, 09, 05, 02]
Raw bytes (43): 0x[01, 01, 02, 01, 05, 01, 09, 07, 01, 03, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 05, 00, 06, 01, 24, 08, 00, 0f, 09, 00, 10, 02, 06, 06, 02, 05, 00, 06, 01, 02, 09, 05, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 2
@ -26,11 +26,11 @@ Number of expressions: 2
Number of file 0 mappings: 7
- Code(Counter(0)) at (prev + 3, 1) to (start + 7, 15)
- Code(Counter(1)) at (prev + 7, 16) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 36, 8) to (start + 0, 15)
- Code(Counter(2)) at (prev + 0, 16) to (start + 2, 6)
- Code(Expression(1, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(1, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c2)
- Code(Counter(0)) at (prev + 2, 9) to (start + 5, 2)
Highest counter ID seen: c2

View File

@ -10,7 +10,7 @@
LL| 1| if is_true {
LL| 1| countdown = 10;
LL| 1| }
^0
^0
LL| |
LL| | mod in_mod {
LL| | const IN_MOD_CONST: u32 = 1000;
@ -49,7 +49,7 @@
LL| 1| if is_true {
LL| 1| in_func(countdown);
LL| 1| }
^0
^0
LL| |
LL| 1| let mut val = InStruct {
LL| 1| in_struct_field: 101, //

View File

@ -1,5 +1,5 @@
Function name: lazy_boolean::main
Raw bytes (158): 0x[01, 01, 07, 01, 05, 01, 09, 01, 0d, 01, 19, 01, 1d, 01, 21, 01, 25, 1c, 01, 04, 01, 07, 0f, 05, 07, 10, 04, 06, 02, 04, 06, 00, 07, 01, 02, 09, 00, 11, 01, 02, 0d, 00, 12, 06, 02, 0d, 00, 12, 01, 03, 09, 00, 11, 01, 02, 0d, 00, 12, 0a, 02, 0d, 00, 12, 01, 02, 09, 00, 11, 01, 00, 14, 00, 19, 11, 00, 1d, 00, 22, 01, 01, 09, 00, 11, 01, 00, 14, 00, 19, 15, 00, 1d, 00, 22, 01, 03, 09, 01, 10, 0e, 02, 05, 03, 06, 19, 03, 06, 00, 07, 01, 03, 09, 00, 10, 1d, 01, 05, 03, 06, 12, 05, 05, 03, 06, 01, 05, 08, 00, 10, 16, 00, 11, 02, 06, 21, 02, 06, 00, 07, 01, 02, 08, 00, 0f, 25, 00, 10, 02, 06, 1a, 02, 0c, 02, 06, 01, 03, 01, 00, 02]
Raw bytes (158): 0x[01, 01, 07, 01, 05, 01, 09, 01, 0d, 01, 19, 01, 1d, 01, 21, 01, 25, 1c, 01, 04, 01, 07, 0f, 05, 07, 10, 04, 06, 02, 04, 05, 00, 06, 01, 02, 09, 00, 11, 01, 02, 0d, 00, 12, 06, 02, 0d, 00, 12, 01, 03, 09, 00, 11, 01, 02, 0d, 00, 12, 0a, 02, 0d, 00, 12, 01, 02, 09, 00, 11, 01, 00, 14, 00, 19, 11, 00, 1d, 00, 22, 01, 01, 09, 00, 11, 01, 00, 14, 00, 19, 15, 00, 1d, 00, 22, 01, 03, 09, 01, 10, 0e, 02, 05, 03, 06, 19, 03, 05, 00, 06, 01, 03, 09, 00, 10, 1d, 01, 05, 03, 06, 12, 05, 05, 03, 06, 01, 05, 08, 00, 10, 16, 00, 11, 02, 06, 21, 02, 05, 00, 06, 01, 02, 08, 00, 0f, 25, 00, 10, 02, 06, 1a, 02, 0c, 02, 06, 01, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 7
@ -13,7 +13,7 @@ Number of expressions: 7
Number of file 0 mappings: 28
- Code(Counter(0)) at (prev + 4, 1) to (start + 7, 15)
- Code(Counter(1)) at (prev + 7, 16) to (start + 4, 6)
- Code(Expression(0, Sub)) at (prev + 4, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 4, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 17)
- Code(Counter(0)) at (prev + 2, 13) to (start + 0, 18)
@ -32,7 +32,7 @@ Number of file 0 mappings: 28
- Code(Counter(0)) at (prev + 3, 9) to (start + 1, 16)
- Code(Expression(3, Sub)) at (prev + 2, 5) to (start + 3, 6)
= (c0 - c6)
- Code(Counter(6)) at (prev + 3, 6) to (start + 0, 7)
- Code(Counter(6)) at (prev + 3, 5) to (start + 0, 6)
- Code(Counter(0)) at (prev + 3, 9) to (start + 0, 16)
- Code(Counter(7)) at (prev + 1, 5) to (start + 3, 6)
- Code(Expression(4, Sub)) at (prev + 5, 5) to (start + 3, 6)
@ -40,7 +40,7 @@ Number of file 0 mappings: 28
- Code(Counter(0)) at (prev + 5, 8) to (start + 0, 16)
- Code(Expression(5, Sub)) at (prev + 0, 17) to (start + 2, 6)
= (c0 - c8)
- Code(Counter(8)) at (prev + 2, 6) to (start + 0, 7)
- Code(Counter(8)) at (prev + 2, 5) to (start + 0, 6)
- Code(Counter(0)) at (prev + 2, 8) to (start + 0, 15)
- Code(Counter(9)) at (prev + 0, 16) to (start + 2, 6)
- Code(Expression(6, Sub)) at (prev + 2, 12) to (start + 2, 6)

View File

@ -13,7 +13,7 @@
LL| 1| b = 10;
LL| 1| c = 100;
LL| 1| }
^0
^0
LL| | let
LL| 1| somebool
LL| | =

View File

@ -1,5 +1,5 @@
Function name: loop_break::main
Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 03, 01, 00, 0b, 03, 02, 0c, 00, 27, 01, 01, 0d, 00, 12, 05, 01, 0a, 00, 0b, 01, 02, 01, 00, 02]
Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 03, 01, 00, 0b, 03, 02, 0c, 00, 27, 01, 01, 0d, 00, 12, 05, 01, 09, 00, 0a, 01, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -9,7 +9,7 @@ Number of file 0 mappings: 5
- Code(Expression(0, Add)) at (prev + 2, 12) to (start + 0, 39)
= (c0 + c1)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 18)
- Code(Counter(1)) at (prev + 1, 10) to (start + 0, 11)
- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 10)
- Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2)
Highest counter ID seen: c1

View File

@ -1,5 +1,5 @@
Function name: <loops_branches::DebugTest as core::fmt::Debug>::fmt
Raw bytes (228): 0x[01, 01, 2a, 05, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, a3, 01, a7, 01, 0d, 00, 11, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 96, 01, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 96, 01, 11, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 8f, 01, 19, 25, 92, 01, 96, 01, 11, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 14, 01, 09, 05, 01, 10, 05, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 02, 01, 0e, 00, 0f, 05, 01, 0d, 00, 1e, 25, 00, 1e, 00, 1f, 00, 01, 10, 01, 0a, 9a, 01, 03, 0d, 00, 0e, 9f, 01, 00, 12, 00, 17, 9a, 01, 01, 10, 00, 14, 96, 01, 01, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 46, 01, 12, 00, 13, 96, 01, 01, 11, 00, 22, 92, 01, 00, 22, 00, 23, 00, 01, 14, 01, 0e, 19, 03, 09, 00, 0f, 8b, 01, 01, 05, 00, 06]
Raw bytes (228): 0x[01, 01, 2a, 05, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, a3, 01, a7, 01, 0d, 00, 11, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 96, 01, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 96, 01, 11, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 8f, 01, 19, 25, 92, 01, 96, 01, 11, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 14, 01, 09, 05, 01, 10, 05, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 02, 01, 0d, 00, 0e, 05, 01, 0d, 00, 1e, 25, 00, 1e, 00, 1f, 00, 01, 10, 01, 0a, 9a, 01, 03, 0d, 00, 0e, 9f, 01, 00, 12, 00, 17, 9a, 01, 01, 10, 00, 14, 96, 01, 01, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 46, 01, 11, 00, 12, 96, 01, 01, 11, 00, 22, 92, 01, 00, 22, 00, 23, 00, 01, 14, 01, 0e, 19, 03, 09, 00, 0f, 8b, 01, 01, 05, 00, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 42
@ -50,7 +50,7 @@ Number of file 0 mappings: 20
- Code(Counter(1)) at (prev + 2, 16) to (start + 0, 21)
- Code(Zero) at (prev + 1, 23) to (start + 0, 27)
- Code(Zero) at (prev + 0, 28) to (start + 0, 30)
- Code(Expression(0, Sub)) at (prev + 1, 14) to (start + 0, 15)
- Code(Expression(0, Sub)) at (prev + 1, 13) to (start + 0, 14)
= (c1 - Zero)
- Code(Counter(1)) at (prev + 1, 13) to (start + 0, 30)
- Code(Counter(9)) at (prev + 0, 30) to (start + 0, 31)
@ -65,7 +65,7 @@ Number of file 0 mappings: 20
= ((((c3 + Zero) + (c4 + Zero)) - c6) - Zero)
- Code(Zero) at (prev + 1, 27) to (start + 0, 31)
- Code(Zero) at (prev + 0, 32) to (start + 0, 34)
- Code(Expression(17, Sub)) at (prev + 1, 18) to (start + 0, 19)
- Code(Expression(17, Sub)) at (prev + 1, 17) to (start + 0, 18)
= (((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) - Zero)
- Code(Expression(37, Sub)) at (prev + 1, 17) to (start + 0, 34)
= ((((c3 + Zero) + (c4 + Zero)) - c6) - Zero)
@ -78,7 +78,7 @@ Number of file 0 mappings: 20
Highest counter ID seen: c9
Function name: <loops_branches::DisplayTest as core::fmt::Display>::fmt
Raw bytes (230): 0x[01, 01, 2b, 01, 00, 02, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, a7, 01, ab, 01, 00, 0d, 00, 15, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9a, 01, 00, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9a, 01, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 93, 01, 25, 96, 01, 19, 9a, 01, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 14, 01, 22, 05, 01, 11, 00, 01, 12, 01, 0a, 02, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 06, 01, 0e, 00, 0f, 02, 01, 0d, 00, 1e, 25, 00, 1e, 00, 1f, 9e, 01, 02, 0d, 00, 0e, a3, 01, 00, 12, 00, 17, 9e, 01, 01, 10, 00, 15, 00, 00, 16, 01, 0e, 9a, 01, 02, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 4a, 01, 12, 00, 13, 9a, 01, 01, 11, 00, 22, 96, 01, 00, 22, 00, 23, 19, 03, 09, 00, 0f, 8f, 01, 01, 05, 00, 06]
Raw bytes (230): 0x[01, 01, 2b, 01, 00, 02, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, a7, 01, ab, 01, 00, 0d, 00, 15, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9a, 01, 00, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9a, 01, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 93, 01, 25, 96, 01, 19, 9a, 01, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 14, 01, 22, 05, 01, 11, 00, 01, 12, 01, 0a, 02, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 06, 01, 0d, 00, 0e, 02, 01, 0d, 00, 1e, 25, 00, 1e, 00, 1f, 9e, 01, 02, 0d, 00, 0e, a3, 01, 00, 12, 00, 17, 9e, 01, 01, 10, 00, 15, 00, 00, 16, 01, 0e, 9a, 01, 02, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 4a, 01, 11, 00, 12, 9a, 01, 01, 11, 00, 22, 96, 01, 00, 22, 00, 23, 19, 03, 09, 00, 0f, 8f, 01, 01, 05, 00, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 43
@ -132,7 +132,7 @@ Number of file 0 mappings: 20
= (c0 - Zero)
- Code(Zero) at (prev + 1, 23) to (start + 0, 27)
- Code(Zero) at (prev + 0, 28) to (start + 0, 30)
- Code(Expression(1, Sub)) at (prev + 1, 14) to (start + 0, 15)
- Code(Expression(1, Sub)) at (prev + 1, 13) to (start + 0, 14)
= ((c0 - Zero) - Zero)
- Code(Expression(0, Sub)) at (prev + 1, 13) to (start + 0, 30)
= (c0 - Zero)
@ -148,7 +148,7 @@ Number of file 0 mappings: 20
= ((((Zero + c3) + (Zero + c5)) - c6) - Zero)
- Code(Zero) at (prev + 1, 27) to (start + 0, 31)
- Code(Zero) at (prev + 0, 32) to (start + 0, 34)
- Code(Expression(18, Sub)) at (prev + 1, 18) to (start + 0, 19)
- Code(Expression(18, Sub)) at (prev + 1, 17) to (start + 0, 18)
= (((((Zero + c3) + (Zero + c5)) - c6) - Zero) - Zero)
- Code(Expression(38, Sub)) at (prev + 1, 17) to (start + 0, 34)
= ((((Zero + c3) + (Zero + c5)) - c6) - Zero)

View File

@ -1,5 +1,5 @@
Function name: match_or_pattern::main
Raw bytes (185): 0x[01, 01, 1c, 01, 05, 09, 0d, 23, 11, 09, 0d, 1f, 15, 23, 11, 09, 0d, 23, 11, 09, 0d, 19, 1d, 43, 21, 19, 1d, 3f, 25, 43, 21, 19, 1d, 43, 21, 19, 1d, 29, 2d, 63, 31, 29, 2d, 5f, 35, 63, 31, 29, 2d, 63, 31, 29, 2d, 39, 3d, 6f, 41, 39, 3d, 19, 01, 01, 01, 08, 0f, 05, 08, 10, 03, 06, 02, 03, 06, 00, 07, 01, 01, 0b, 00, 11, 11, 03, 1b, 00, 1d, 23, 01, 0e, 00, 10, 1f, 02, 08, 00, 0f, 15, 00, 10, 03, 06, 12, 03, 06, 00, 07, 1f, 01, 0b, 00, 11, 21, 01, 1b, 00, 1d, 43, 01, 0e, 00, 10, 3f, 02, 08, 00, 0f, 25, 00, 10, 03, 06, 32, 03, 06, 00, 07, 3f, 01, 0b, 00, 11, 31, 01, 1b, 00, 1d, 63, 01, 0e, 00, 10, 5f, 02, 08, 00, 0f, 35, 00, 10, 03, 06, 52, 03, 06, 00, 07, 5f, 01, 0b, 00, 11, 41, 01, 1b, 00, 1d, 6f, 01, 0e, 00, 10, 6b, 02, 01, 00, 02]
Raw bytes (185): 0x[01, 01, 1c, 01, 05, 09, 0d, 23, 11, 09, 0d, 1f, 15, 23, 11, 09, 0d, 23, 11, 09, 0d, 19, 1d, 43, 21, 19, 1d, 3f, 25, 43, 21, 19, 1d, 43, 21, 19, 1d, 29, 2d, 63, 31, 29, 2d, 5f, 35, 63, 31, 29, 2d, 63, 31, 29, 2d, 39, 3d, 6f, 41, 39, 3d, 19, 01, 01, 01, 08, 0f, 05, 08, 10, 03, 06, 02, 03, 05, 00, 06, 01, 01, 0b, 00, 11, 11, 03, 1b, 00, 1d, 23, 01, 0e, 00, 10, 1f, 02, 08, 00, 0f, 15, 00, 10, 03, 06, 12, 03, 05, 00, 06, 1f, 01, 0b, 00, 11, 21, 01, 1b, 00, 1d, 43, 01, 0e, 00, 10, 3f, 02, 08, 00, 0f, 25, 00, 10, 03, 06, 32, 03, 05, 00, 06, 3f, 01, 0b, 00, 11, 31, 01, 1b, 00, 1d, 63, 01, 0e, 00, 10, 5f, 02, 08, 00, 0f, 35, 00, 10, 03, 06, 52, 03, 05, 00, 06, 5f, 01, 0b, 00, 11, 41, 01, 1b, 00, 1d, 6f, 01, 0e, 00, 10, 6b, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 28
@ -34,7 +34,7 @@ Number of expressions: 28
Number of file 0 mappings: 25
- Code(Counter(0)) at (prev + 1, 1) to (start + 8, 15)
- Code(Counter(1)) at (prev + 8, 16) to (start + 3, 6)
- Code(Expression(0, Sub)) at (prev + 3, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 3, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 11) to (start + 0, 17)
- Code(Counter(4)) at (prev + 3, 27) to (start + 0, 29)
@ -43,7 +43,7 @@ Number of file 0 mappings: 25
- Code(Expression(7, Add)) at (prev + 2, 8) to (start + 0, 15)
= ((c2 + c3) + c4)
- Code(Counter(5)) at (prev + 0, 16) to (start + 3, 6)
- Code(Expression(4, Sub)) at (prev + 3, 6) to (start + 0, 7)
- Code(Expression(4, Sub)) at (prev + 3, 5) to (start + 0, 6)
= (((c2 + c3) + c4) - c5)
- Code(Expression(7, Add)) at (prev + 1, 11) to (start + 0, 17)
= ((c2 + c3) + c4)
@ -53,7 +53,7 @@ Number of file 0 mappings: 25
- Code(Expression(15, Add)) at (prev + 2, 8) to (start + 0, 15)
= ((c6 + c7) + c8)
- Code(Counter(9)) at (prev + 0, 16) to (start + 3, 6)
- Code(Expression(12, Sub)) at (prev + 3, 6) to (start + 0, 7)
- Code(Expression(12, Sub)) at (prev + 3, 5) to (start + 0, 6)
= (((c6 + c7) + c8) - c9)
- Code(Expression(15, Add)) at (prev + 1, 11) to (start + 0, 17)
= ((c6 + c7) + c8)
@ -63,7 +63,7 @@ Number of file 0 mappings: 25
- Code(Expression(23, Add)) at (prev + 2, 8) to (start + 0, 15)
= ((c10 + c11) + c12)
- Code(Counter(13)) at (prev + 0, 16) to (start + 3, 6)
- Code(Expression(20, Sub)) at (prev + 3, 6) to (start + 0, 7)
- Code(Expression(20, Sub)) at (prev + 3, 5) to (start + 0, 6)
= (((c10 + c11) + c12) - c13)
- Code(Expression(23, Add)) at (prev + 1, 11) to (start + 0, 17)
= ((c10 + c11) + c12)

View File

@ -10,7 +10,7 @@
LL| 1| a = 2;
LL| 1| b = 0;
LL| 1| }
^0
^0
LL| 1| match (a, b) {
LL| | // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`.
LL| | // This test confirms a fix for Issue #79569.
@ -21,7 +21,7 @@
LL| 1| a = 0;
LL| 1| b = 0;
LL| 1| }
^0
^0
LL| 1| match (a, b) {
LL| 0| (0 | 1, 2 | 3) => {}
LL| 1| _ => {}
@ -30,7 +30,7 @@
LL| 1| a = 2;
LL| 1| b = 2;
LL| 1| }
^0
^0
LL| 1| match (a, b) {
LL| 0| (0 | 1, 2 | 3) => {}
LL| 1| _ => {}
@ -39,7 +39,7 @@
LL| 1| a = 0;
LL| 1| b = 2;
LL| 1| }
^0
^0
LL| 1| match (a, b) {
LL| 1| (0 | 1, 2 | 3) => {}
LL| 0| _ => {}

View File

@ -1,5 +1,5 @@
Function name: condition_limit::accept_7_conditions
Raw bytes (232): 0x[01, 01, 2c, 01, 05, 05, 1d, 05, 1d, 7a, 19, 05, 1d, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 21, 9b, 01, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 12, 01, 07, 01, 02, 09, 28, 08, 07, 02, 08, 00, 27, 30, 05, 02, 01, 07, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 7a, 1d, 07, 06, 00, 00, 0d, 00, 0e, 7a, 00, 12, 00, 13, 30, 76, 19, 06, 05, 00, 00, 12, 00, 13, 76, 00, 17, 00, 18, 30, 72, 15, 05, 04, 00, 00, 17, 00, 18, 72, 00, 1c, 00, 1d, 30, 6e, 11, 04, 03, 00, 00, 1c, 00, 1d, 6e, 00, 21, 00, 22, 30, 6a, 0d, 03, 02, 00, 00, 21, 00, 22, 6a, 00, 26, 00, 27, 30, 21, 09, 02, 00, 00, 00, 26, 00, 27, 21, 00, 28, 02, 06, 9b, 01, 02, 06, 00, 07, 97, 01, 01, 01, 00, 02]
Raw bytes (232): 0x[01, 01, 2c, 01, 05, 05, 1d, 05, 1d, 7a, 19, 05, 1d, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 21, 9b, 01, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 12, 01, 07, 01, 02, 09, 28, 08, 07, 02, 08, 00, 27, 30, 05, 02, 01, 07, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 7a, 1d, 07, 06, 00, 00, 0d, 00, 0e, 7a, 00, 12, 00, 13, 30, 76, 19, 06, 05, 00, 00, 12, 00, 13, 76, 00, 17, 00, 18, 30, 72, 15, 05, 04, 00, 00, 17, 00, 18, 72, 00, 1c, 00, 1d, 30, 6e, 11, 04, 03, 00, 00, 1c, 00, 1d, 6e, 00, 21, 00, 22, 30, 6a, 0d, 03, 02, 00, 00, 21, 00, 22, 6a, 00, 26, 00, 27, 30, 21, 09, 02, 00, 00, 00, 26, 00, 27, 21, 00, 28, 02, 06, 9b, 01, 02, 05, 00, 06, 97, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 44
@ -83,7 +83,7 @@ Number of file 0 mappings: 18
true = c8
false = c2
- Code(Counter(8)) at (prev + 0, 40) to (start + 2, 6)
- Code(Expression(38, Add)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(38, Add)) at (prev + 2, 5) to (start + 0, 6)
= ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1))
- Code(Expression(37, Add)) at (prev + 1, 1) to (start + 0, 2)
= (c8 + ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1)))

View File

@ -175,7 +175,7 @@ Number of file 0 mappings: 10
Highest counter ID seen: c4
Function name: if::mcdc_nested_if
Raw bytes (124): 0x[01, 01, 0d, 01, 05, 02, 09, 05, 09, 1b, 15, 05, 09, 1b, 15, 05, 09, 11, 15, 02, 09, 2b, 32, 0d, 2f, 11, 15, 02, 09, 0e, 01, 3b, 01, 01, 09, 28, 03, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 00, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 30, 09, 32, 02, 00, 00, 00, 0d, 00, 0e, 1b, 01, 09, 01, 0d, 28, 06, 02, 01, 0c, 00, 12, 30, 16, 15, 01, 02, 00, 00, 0c, 00, 0d, 16, 00, 11, 00, 12, 30, 0d, 11, 02, 00, 00, 00, 11, 00, 12, 0d, 00, 13, 02, 0a, 2f, 02, 0a, 00, 0b, 32, 01, 0c, 02, 06, 27, 03, 01, 00, 02]
Raw bytes (124): 0x[01, 01, 0d, 01, 05, 02, 09, 05, 09, 1b, 15, 05, 09, 1b, 15, 05, 09, 11, 15, 02, 09, 2b, 32, 0d, 2f, 11, 15, 02, 09, 0e, 01, 3b, 01, 01, 09, 28, 03, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 00, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 30, 09, 32, 02, 00, 00, 00, 0d, 00, 0e, 1b, 01, 09, 01, 0d, 28, 06, 02, 01, 0c, 00, 12, 30, 16, 15, 01, 02, 00, 00, 0c, 00, 0d, 16, 00, 11, 00, 12, 30, 0d, 11, 02, 00, 00, 00, 11, 00, 12, 0d, 00, 13, 02, 0a, 2f, 02, 09, 00, 0a, 32, 01, 0c, 02, 06, 27, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 13
@ -215,7 +215,7 @@ Number of file 0 mappings: 14
true = c3
false = c4
- Code(Counter(3)) at (prev + 0, 19) to (start + 2, 10)
- Code(Expression(11, Add)) at (prev + 2, 10) to (start + 0, 11)
- Code(Expression(11, Add)) at (prev + 2, 9) to (start + 0, 10)
= (c4 + c5)
- Code(Expression(12, Sub)) at (prev + 1, 12) to (start + 2, 6)
= ((c0 - c1) - c2)

View File

@ -1,5 +1,5 @@
Function name: nested_loops::main
Raw bytes (115): 0x[01, 01, 17, 01, 57, 05, 09, 03, 0d, 4e, 53, 03, 0d, 15, 19, 4b, 09, 4e, 53, 03, 0d, 15, 19, 46, 05, 4b, 09, 4e, 53, 03, 0d, 15, 19, 42, 19, 46, 05, 4b, 09, 4e, 53, 03, 0d, 15, 19, 05, 09, 11, 0d, 0d, 01, 01, 01, 02, 1b, 03, 04, 13, 00, 20, 4e, 01, 0d, 01, 18, 4b, 02, 12, 00, 17, 46, 01, 10, 00, 16, 05, 01, 11, 00, 16, 42, 01, 0e, 03, 16, 3e, 04, 11, 01, 1b, 11, 02, 15, 00, 21, 15, 01, 18, 02, 12, 19, 03, 0e, 00, 0f, 57, 02, 09, 00, 17, 5b, 02, 01, 00, 02]
Raw bytes (115): 0x[01, 01, 17, 01, 57, 05, 09, 03, 0d, 4e, 53, 03, 0d, 15, 19, 4b, 09, 4e, 53, 03, 0d, 15, 19, 46, 05, 4b, 09, 4e, 53, 03, 0d, 15, 19, 42, 19, 46, 05, 4b, 09, 4e, 53, 03, 0d, 15, 19, 05, 09, 11, 0d, 0d, 01, 01, 01, 02, 1b, 03, 04, 13, 00, 20, 4e, 01, 0d, 01, 18, 4b, 02, 12, 00, 17, 46, 01, 10, 00, 16, 05, 01, 11, 00, 16, 42, 01, 0e, 03, 16, 3e, 04, 11, 01, 1b, 11, 02, 15, 00, 21, 15, 01, 18, 02, 12, 19, 03, 0d, 00, 0e, 57, 02, 09, 00, 17, 5b, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 23
@ -43,7 +43,7 @@ Number of file 0 mappings: 13
= ((((((c0 + (c1 + c2)) - c3) + (c5 + c6)) - c2) - c1) - c6)
- Code(Counter(4)) at (prev + 2, 21) to (start + 0, 33)
- Code(Counter(5)) at (prev + 1, 24) to (start + 2, 18)
- Code(Counter(6)) at (prev + 3, 14) to (start + 0, 15)
- Code(Counter(6)) at (prev + 3, 13) to (start + 0, 14)
- Code(Expression(21, Add)) at (prev + 2, 9) to (start + 0, 23)
= (c1 + c2)
- Code(Expression(22, Add)) at (prev + 2, 1) to (start + 0, 2)

View File

@ -1,5 +1,5 @@
Function name: overflow::main
Raw bytes (65): 0x[01, 01, 08, 01, 1b, 05, 1f, 09, 0d, 03, 11, 16, 05, 03, 11, 05, 1f, 09, 0d, 09, 01, 10, 01, 01, 1b, 03, 02, 0b, 00, 18, 16, 01, 0c, 00, 1a, 05, 00, 1b, 03, 0a, 12, 03, 13, 00, 20, 09, 00, 21, 03, 0a, 0d, 03, 0a, 00, 0b, 1b, 01, 09, 00, 17, 11, 02, 05, 01, 02]
Raw bytes (65): 0x[01, 01, 08, 01, 1b, 05, 1f, 09, 0d, 03, 11, 16, 05, 03, 11, 05, 1f, 09, 0d, 09, 01, 10, 01, 01, 1b, 03, 02, 0b, 00, 18, 16, 01, 0c, 00, 1a, 05, 00, 1b, 03, 0a, 12, 03, 13, 00, 20, 09, 00, 21, 03, 0a, 0d, 03, 09, 00, 0a, 1b, 01, 09, 00, 17, 11, 02, 05, 01, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 8
@ -21,14 +21,14 @@ Number of file 0 mappings: 9
- Code(Expression(4, Sub)) at (prev + 3, 19) to (start + 0, 32)
= (((c0 + (c1 + (c2 + c3))) - c4) - c1)
- Code(Counter(2)) at (prev + 0, 33) to (start + 3, 10)
- Code(Counter(3)) at (prev + 3, 10) to (start + 0, 11)
- Code(Counter(3)) at (prev + 3, 9) to (start + 0, 10)
- Code(Expression(6, Add)) at (prev + 1, 9) to (start + 0, 23)
= (c1 + (c2 + c3))
- Code(Counter(4)) at (prev + 2, 5) to (start + 1, 2)
Highest counter ID seen: c4
Function name: overflow::might_overflow
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 05, 01, 01, 12, 05, 01, 13, 02, 06, 02, 02, 06, 00, 07, 01, 01, 09, 05, 02]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 05, 01, 01, 12, 05, 01, 13, 02, 06, 02, 02, 05, 00, 06, 01, 01, 09, 05, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -36,7 +36,7 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 5, 1) to (start + 1, 18)
- Code(Counter(1)) at (prev + 1, 19) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 9) to (start + 5, 2)
Highest counter ID seen: c1

View File

@ -1,5 +1,5 @@
Function name: panic_unwind::main
Raw bytes (65): 0x[01, 01, 08, 01, 1b, 05, 1f, 09, 0d, 03, 11, 16, 05, 03, 11, 05, 1f, 09, 0d, 09, 01, 0d, 01, 01, 1b, 03, 02, 0b, 00, 18, 16, 01, 0c, 00, 1a, 05, 00, 1b, 02, 0a, 12, 02, 13, 00, 20, 09, 00, 21, 02, 0a, 0d, 02, 0a, 00, 0b, 1b, 01, 09, 00, 17, 11, 02, 05, 01, 02]
Raw bytes (65): 0x[01, 01, 08, 01, 1b, 05, 1f, 09, 0d, 03, 11, 16, 05, 03, 11, 05, 1f, 09, 0d, 09, 01, 0d, 01, 01, 1b, 03, 02, 0b, 00, 18, 16, 01, 0c, 00, 1a, 05, 00, 1b, 02, 0a, 12, 02, 13, 00, 20, 09, 00, 21, 02, 0a, 0d, 02, 09, 00, 0a, 1b, 01, 09, 00, 17, 11, 02, 05, 01, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 8
@ -21,7 +21,7 @@ Number of file 0 mappings: 9
- Code(Expression(4, Sub)) at (prev + 2, 19) to (start + 0, 32)
= (((c0 + (c1 + (c2 + c3))) - c4) - c1)
- Code(Counter(2)) at (prev + 0, 33) to (start + 2, 10)
- Code(Counter(3)) at (prev + 2, 10) to (start + 0, 11)
- Code(Counter(3)) at (prev + 2, 9) to (start + 0, 10)
- Code(Expression(6, Add)) at (prev + 1, 9) to (start + 0, 23)
= (c1 + (c2 + c3))
- Code(Counter(4)) at (prev + 2, 5) to (start + 1, 2)

View File

@ -1,5 +1,5 @@
Function name: simple_loop::main
Raw bytes (43): 0x[01, 01, 02, 01, 05, 01, 09, 07, 01, 04, 01, 09, 10, 05, 0a, 05, 05, 06, 02, 05, 06, 00, 07, 07, 05, 0d, 02, 0e, 01, 04, 0d, 00, 12, 09, 02, 0a, 03, 0a, 01, 06, 01, 00, 02]
Raw bytes (43): 0x[01, 01, 02, 01, 05, 01, 09, 07, 01, 04, 01, 09, 10, 05, 0a, 05, 05, 06, 02, 05, 05, 00, 06, 07, 05, 0d, 02, 0e, 01, 04, 0d, 00, 12, 09, 02, 0a, 03, 0a, 01, 06, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 2
@ -8,7 +8,7 @@ Number of expressions: 2
Number of file 0 mappings: 7
- Code(Counter(0)) at (prev + 4, 1) to (start + 9, 16)
- Code(Counter(1)) at (prev + 10, 5) to (start + 5, 6)
- Code(Expression(0, Sub)) at (prev + 5, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 5, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Expression(1, Add)) at (prev + 5, 13) to (start + 2, 14)
= (c0 + c2)

View File

@ -17,7 +17,7 @@
LL| 1| 10
LL| 1| ;
LL| 1| }
^0
^0
LL| |
LL| | loop
LL| | {

View File

@ -1,5 +1,5 @@
Function name: simple_match::main
Raw bytes (72): 0x[01, 01, 09, 01, 05, 01, 23, 09, 0d, 1f, 11, 01, 23, 09, 0d, 1f, 11, 01, 23, 09, 0d, 0a, 01, 04, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 1f, 05, 09, 00, 0d, 1a, 05, 0d, 00, 16, 09, 02, 0d, 00, 0e, 1a, 02, 11, 02, 12, 09, 04, 0d, 07, 0e, 0d, 0a, 0d, 00, 0f, 11, 03, 01, 00, 02]
Raw bytes (72): 0x[01, 01, 09, 01, 05, 01, 23, 09, 0d, 1f, 11, 01, 23, 09, 0d, 1f, 11, 01, 23, 09, 0d, 0a, 01, 04, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 05, 00, 06, 1f, 05, 09, 00, 0d, 1a, 05, 0d, 00, 16, 09, 02, 0d, 00, 0e, 1a, 02, 11, 02, 12, 09, 04, 0d, 07, 0e, 0d, 0a, 0d, 00, 0f, 11, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 9
@ -15,7 +15,7 @@ Number of expressions: 9
Number of file 0 mappings: 10
- Code(Counter(0)) at (prev + 4, 1) to (start + 7, 15)
- Code(Counter(1)) at (prev + 7, 16) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Expression(7, Add)) at (prev + 5, 9) to (start + 0, 13)
= (c0 + (c2 + c3))

View File

@ -11,7 +11,7 @@
LL| 1| if is_true {
LL| 1| countdown = 0;
LL| 1| }
^0
^0
LL| |
LL| | for
LL| | _

View File

@ -1,5 +1,5 @@
Function name: sort_groups::generic_fn::<&str>
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 05, 00, 06, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -7,13 +7,13 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 17, 1) to (start + 1, 12)
- Code(Counter(1)) at (prev + 1, 13) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1
Function name: sort_groups::generic_fn::<()>
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 05, 00, 06, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -21,13 +21,13 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 17, 1) to (start + 1, 12)
- Code(Counter(1)) at (prev + 1, 13) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1
Function name: sort_groups::generic_fn::<char>
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 05, 00, 06, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -35,13 +35,13 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 17, 1) to (start + 1, 12)
- Code(Counter(1)) at (prev + 1, 13) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1
Function name: sort_groups::generic_fn::<i32>
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 05, 00, 06, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -49,13 +49,13 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 17, 1) to (start + 1, 12)
- Code(Counter(1)) at (prev + 1, 13) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1
Function name: sort_groups::main
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 06, 01, 04, 23, 05, 04, 24, 02, 06, 02, 02, 06, 00, 07, 01, 01, 05, 02, 02]
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 06, 01, 04, 23, 05, 04, 24, 02, 06, 02, 02, 05, 00, 06, 01, 01, 05, 02, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
@ -63,7 +63,7 @@ Number of expressions: 1
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 6, 1) to (start + 4, 35)
- Code(Counter(1)) at (prev + 4, 36) to (start + 2, 6)
- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
= (c0 - c1)
- Code(Counter(0)) at (prev + 1, 5) to (start + 2, 2)
Highest counter ID seen: c1

View File

@ -27,7 +27,7 @@
| LL| 1| if cond {
| LL| 1| println!("{}", std::any::type_name::<T>());
| LL| 1| }
| ^0
| ^0
| LL| 1|}
------------------
| sort_groups::generic_fn::<()>:

Some files were not shown because too many files have changed in this diff Show More