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:
commit
2128d8df0e
@ -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"
|
||||
|
@ -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}");
|
||||
|
@ -6,7 +6,6 @@
|
||||
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 @@ pub(crate) fn into_finished(self) -> FunctionCoverage<'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 @@ pub(crate) fn source_hash(&self) -> u64 {
|
||||
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(
|
||||
|
@ -12,8 +12,10 @@
|
||||
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 @@ fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self {
|
||||
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 @@ fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
|
||||
}
|
||||
|
||||
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 @@ fn local_id_for_global(&mut self, global_file_id: u32) -> LocalFileId {
|
||||
}
|
||||
|
||||
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),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
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 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
#[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>,
|
||||
|
@ -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:?};")?;
|
||||
}
|
||||
|
@ -22,8 +22,8 @@
|
||||
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.
|
||||
|
@ -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)]
|
||||
|
@ -534,7 +534,7 @@ pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
|
||||
/// 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>,
|
||||
{
|
||||
|
@ -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" }
|
||||
|
13
src/tools/miri/.github/workflows/ci.yml
vendored
13
src/tools/miri/.github/workflows/ci.yml
vendored
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
arithmetic-side-effects-allowed = ["rustc_abi::Size"]
|
||||
arithmetic-side-effects-allowed = ["rustc_abi::Size", "rustc_apfloat::ieee::IeeeFloat"]
|
||||
|
@ -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"
|
||||
|
@ -24,3 +24,4 @@ rustc_version = "0.4"
|
||||
dunce = "1.0.4"
|
||||
directories = "5"
|
||||
serde_json = "1"
|
||||
tempfile = "3.13.0"
|
||||
|
@ -172,7 +172,8 @@ pub fn exec(self) -> Result<()> {
|
||||
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 @@ fn clippy(flags: Vec<String>) -> Result<()> {
|
||||
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 @@ fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<(
|
||||
// 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(())
|
||||
}
|
||||
|
||||
|
91
src/tools/miri/miri-script/src/coverage.rs
Normal file
91
src/tools/miri/miri-script/src/coverage.rs
Normal 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 binary’s 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())
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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 @@ pub fn new() -> Result<Self> {
|
||||
// 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 @@ pub fn new() -> Result<Self> {
|
||||
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<'_> {
|
||||
|
@ -1 +1 @@
|
||||
814df6e50eaf89b90793e7d9618bb60f1f18377a
|
||||
668959740f97e7a22ae340742886d330ab63950f
|
||||
|
@ -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 @@ fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option<AllocId> {
|
||||
// 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 @@ fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option<AllocId> {
|
||||
// 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 @@ fn addr_from_alloc_id_uncached(
|
||||
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 @@ fn addr_from_alloc_id_uncached(
|
||||
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 @@ fn addr_from_alloc_id_uncached(
|
||||
.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 @@ fn addr_from_alloc_id_uncached(
|
||||
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 @@ fn addr_from_alloc_id_uncached(
|
||||
.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 @@ fn addr_from_alloc_id(
|
||||
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 @@ fn addr_from_alloc_id(
|
||||
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 @@ fn expose_ptr(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'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 @@ fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer> {
|
||||
}
|
||||
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 @@ fn adjust_alloc_root_pointer(
|
||||
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 @@ fn get_global_alloc_bytes(
|
||||
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 @@ fn ptr_get_alloc(
|
||||
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 @@ fn ptr_get_alloc(
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
@ -354,7 +354,7 @@ pub fn get(&self, idx: usize) -> Option<Item> {
|
||||
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()
|
||||
}
|
||||
|
@ -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 @@ pub fn init_once_create(&mut self) -> InitOnceId {
|
||||
}
|
||||
|
||||
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 @@ fn lazy_sync_get_data<T: 'static + Copy>(
|
||||
|
||||
/// 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 @@ fn condvar_signal(&mut self, id: CondvarId) -> InterpResult<'tcx, bool> {
|
||||
/// 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 @@ fn futex_wait(
|
||||
@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 @@ fn futex_wait(
|
||||
);
|
||||
}
|
||||
|
||||
/// 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 @@ fn futex_wake(&mut self, addr: u64, bitset: u32) -> InterpResult<'tcx, bool> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ fn index(self) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ pub(crate) fn init(
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ pub fn active_thread_stack_mut(
|
||||
) -> &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 @@ fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
|
||||
// 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 @@ fn active_thread_stack_mut<'a>(
|
||||
/// 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]
|
||||
|
@ -300,7 +300,6 @@ fn buffered_write(
|
||||
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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -292,7 +292,6 @@ fn emulate_intrinsic_by_name(
|
||||
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 @@ fn emulate_intrinsic_by_name(
|
||||
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()
|
||||
|
@ -750,7 +750,6 @@ enum Op {
|
||||
|
||||
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 @@ enum Op {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +89,18 @@ fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -21,7 +21,7 @@
|
||||
#[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 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
.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 @@ fn emulate_foreign_item_inner(
|
||||
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).
|
||||
|
@ -7,6 +7,7 @@
|
||||
#[derive(Debug)]
|
||||
pub enum IoError {
|
||||
LibcError(&'static str),
|
||||
WindowsError(&'static str),
|
||||
HostError(io::Error),
|
||||
Raw(Scalar),
|
||||
}
|
||||
@ -113,6 +114,7 @@ fn set_last_error(&mut self, err: impl Into<IoError>) -> InterpResult<'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 @@ fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar>
|
||||
}
|
||||
|
||||
/// The inverse of `io_error_to_errnum`.
|
||||
#[allow(clippy::needless_return)]
|
||||
#[expect(clippy::needless_return)]
|
||||
fn try_errnum_to_io_error(
|
||||
&self,
|
||||
errnum: Scalar,
|
||||
|
@ -53,7 +53,7 @@ fn default() -> Self {
|
||||
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>>,
|
||||
|
@ -2,6 +2,7 @@
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
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)?,
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
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),
|
||||
|
@ -603,13 +603,13 @@ fn emulate_foreign_item_inner(
|
||||
}
|
||||
"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)?;
|
||||
|
@ -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;
|
||||
|
@ -4,8 +4,7 @@
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
"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 @@ fn emulate_foreign_item_inner(
|
||||
// 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 @@ fn emulate_foreign_item_inner(
|
||||
|
||||
// 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
|
||||
|
@ -22,7 +22,7 @@ fn mremap(
|
||||
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"));
|
||||
|
@ -3,3 +3,4 @@
|
||||
pub mod foreign_items;
|
||||
pub mod mem;
|
||||
pub mod sync;
|
||||
pub mod syscall;
|
||||
|
@ -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;
|
||||
|
63
src/tools/miri/src/shims/unix/linux/syscall.rs
Normal file
63
src/tools/miri/src/shims/unix/linux/syscall.rs
Normal 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(())
|
||||
}
|
@ -181,18 +181,16 @@ fn emulate_foreign_item_inner(
|
||||
// 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 @@ fn emulate_foreign_item_inner(
|
||||
// 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)?;
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ fn munmap(&mut self, addr: &OpTy<'tcx>, length: &OpTy<'tcx>) -> InterpResult<'tc
|
||||
|
||||
// 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"));
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
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.
|
||||
|
@ -26,26 +26,33 @@ fn emulate_foreign_item_inner(
|
||||
// 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)?;
|
||||
}
|
||||
|
||||
|
@ -685,7 +685,6 @@ fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'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 {
|
||||
|
@ -2,6 +2,13 @@
|
||||
|
||||
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 @@ fn pthread_create(
|
||||
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 @@ fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpRe
|
||||
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 @@ fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
|
||||
|
||||
/// 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 @@ fn pthread_setname_np(
|
||||
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 @@ fn pthread_getname_np(
|
||||
};
|
||||
|
||||
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, ()> {
|
||||
|
@ -10,6 +10,10 @@
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
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] =
|
||||
|
@ -14,7 +14,7 @@ pub enum PseudoHandle {
|
||||
pub enum Handle {
|
||||
Null,
|
||||
Pseudo(PseudoHandle),
|
||||
Thread(ThreadId),
|
||||
Thread(u32),
|
||||
}
|
||||
|
||||
impl PseudoHandle {
|
||||
@ -51,7 +51,7 @@ fn data(self) -> u32 {
|
||||
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 @@ fn packed_disc_size() -> u32 {
|
||||
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 @@ fn to_packed(self) -> u32 {
|
||||
|
||||
// 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 @@ fn from_packed(handle: u32) -> Option<Self> {
|
||||
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 @@ fn from_packed(handle: u32) -> Option<Self> {
|
||||
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 @@ pub fn from_scalar<'tcx>(
|
||||
) -> 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 @@ fn invalid_handle(&mut self, function_name: &str) -> InterpResult<'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)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
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 @@ fn WaitOnAddress(
|
||||
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 @@ fn WaitOnAddress(
|
||||
|
||||
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 @@ fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'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 @@ fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'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(())
|
||||
}
|
||||
|
@ -59,14 +59,18 @@ fn WaitForSingleObject(
|
||||
&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 @@ fn WaitForSingleObject(
|
||||
|
||||
this.join_thread(thread)?;
|
||||
|
||||
interp_ok(0)
|
||||
interp_ok(this.eval_windows("c", "WAIT_OBJECT_0"))
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ fn affine_transform<'tcx>(
|
||||
/// 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
|
||||
|
@ -95,11 +95,22 @@ fn emulate_x86_intrinsic(
|
||||
}
|
||||
}
|
||||
|
||||
"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(())
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@
|
||||
/// 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 @@ fn emulate_x86_sse42_intrinsic(
|
||||
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())
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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 @@ fn clone(&self) -> Self {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -199,4 +199,29 @@ fn get_thread_name(name: &mut [u8]) -> i32 {
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
193
src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs
Normal file
193
src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs
Normal 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))
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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(())
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
@ -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|}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -34,6 +34,6 @@
|
||||
LL| 1| if is_true {
|
||||
LL| 1| countdown = 10;
|
||||
LL| 1| }
|
||||
^0
|
||||
^0
|
||||
LL| 1|}
|
||||
|
||||
|
@ -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
|
||||
|
@ -26,6 +26,6 @@
|
||||
LL| 1| 10
|
||||
LL| 1| ;
|
||||
LL| 1| }
|
||||
^0
|
||||
^0
|
||||
LL| 1|}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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, //
|
||||
|
@ -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)
|
||||
|
@ -13,7 +13,7 @@
|
||||
LL| 1| b = 10;
|
||||
LL| 1| c = 100;
|
||||
LL| 1| }
|
||||
^0
|
||||
^0
|
||||
LL| | let
|
||||
LL| 1| somebool
|
||||
LL| | =
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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| _ => {}
|
||||
|
@ -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)))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -17,7 +17,7 @@
|
||||
LL| 1| 10
|
||||
LL| 1| ;
|
||||
LL| 1| }
|
||||
^0
|
||||
^0
|
||||
LL| |
|
||||
LL| | loop
|
||||
LL| | {
|
||||
|
@ -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))
|
||||
|
@ -11,7 +11,7 @@
|
||||
LL| 1| if is_true {
|
||||
LL| 1| countdown = 0;
|
||||
LL| 1| }
|
||||
^0
|
||||
^0
|
||||
LL| |
|
||||
LL| | for
|
||||
LL| | _
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user